
Para obter a mesma saída que você anotou na sua pergunta, tudo o que é necessário é o seguinte:
PS1='${PS2c##*[$((PS2c=0))-9]}- > '
PS2='$((PS2c=PS2c+1)) > '
Você não precisa se contorcer. Essas duas linhas farão tudo em qualquer shell que pretenda algo próximo à compatibilidade com POSIX.
- > cat <<HD
1 > line 1
2 > line $((PS2c-1))
3 > HD
line 1
line 2
- > echo $PS2c
0
Mas gostei disso. E eu queria demonstrar os fundamentos do que torna esse trabalho um pouco melhor. Então eu editei isso um pouco. Fiquei preso /tmppor agora, mas acho que vou mantê-lo para mim também. Está aqui:
cat /tmp/prompt
PROMPT SCRIPT:
ps1() { IFS=/
set -- ${PWD%"${last=${PWD##/*/}}"}
printf "${1+%c/}" "$@"
printf "$last > "
}
PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'
PS2='$((PS2c=PS2c+1)) > '
Nota: tendo aprendido recentemente sobre yash , eu o construí ontem. Por qualquer motivo, ele não imprime o primeiro byte de cada argumento com a %cstring - embora os documentos sejam específicos sobre extensões de caracteres largos para esse formato e, portanto, talvez relacionados - mas funciona bem com%.1s
Essa é a coisa toda. Há duas coisas principais acontecendo lá em cima. E é assim que se parece:
/u/s/m/man3 > cat <<HERE
1 > line 1
2 > line 2
3 > line $((PS2c-1))
4 > HERE
line 1
line 2
line 3
/u/s/m/man3 >
ANÁLISE $PWD
Toda vez que $PS1é avaliada, ele analisa e imprime $PWDpara adicionar ao prompt. Mas eu não gosto da $PWDmultidão toda na minha tela, então quero apenas a primeira letra de cada trilha de navegação no caminho atual até o diretório atual, que eu gostaria de ver na íntegra. Como isso:
/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cd /
/ > cd ~
/h/mikeserv >
Existem alguns passos aqui:
IFS=/
teremos que dividir a atual $PWDe a maneira mais confiável de fazer isso é com a $IFSdivisão ativada /. Não há necessidade de se preocupar com isso depois - todas as divisões daqui em diante serão definidas pela $@matriz de parâmetros posicionais do shell no próximo comando, como:
set -- ${PWD%"${last=${PWD##/*/}}"}
Então, este é um pouco complicado, mas o principal é que estamos dividindo $PWDem /símbolos. Também uso a expansão de parâmetros para atribuir a $lasttudo após qualquer valor entre a /barra mais à esquerda e mais à direita . Dessa forma, eu sei que se eu estiver apenas com /apenas um /, $lastainda será igual ao todo $PWDe $1ficará vazio. Isso importa. Também retiro $lasto final da cauda $PWDantes de atribuí-lo $@.
printf "${1+%c/}" "$@"
Então aqui - contanto ${1+is set}que seja printfo primeiro a %ccaracterizar cada um dos argumentos de nosso shell - que acabamos de definir para cada diretório em nosso atual $PWD- menos o diretório superior - nos separamos /. Portanto, estamos basicamente apenas imprimindo o primeiro caractere de cada diretório, $PWDexceto o principal. É importante, no entanto, perceber que isso só acontece se $1for definido, o que não acontecerá na raiz /ou em uma remoção /como a /etc.
printf "$last > "
$lasté a variável que acabei de atribuir ao nosso diretório superior. Então agora este é o nosso diretório principal. Imprime se a última declaração foi ou não. E é preciso muito pouco >para uma boa medida.
Mas e quanto ao aumento?
E depois há a questão do $PS2condicional. Eu mostrei anteriormente como isso pode ser feito, que você ainda encontra abaixo - isso é fundamentalmente uma questão de escopo. Mas há um pouco mais, a menos que você queira começar a fazer um monte de printf \bespaços de ação e tentar equilibrar a contagem de caracteres ... ugh. Então eu faço isso:
PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'
Mais uma vez, ${parameter##expansion}salva o dia. É um pouco estranho aqui - na verdade, definimos a variável enquanto a removemos por si mesma. Usamos seu novo valor - definido no meio da faixa - como o globo do qual tiramos a faixa. Entende? Tiramos ##*tudo da cabeça da nossa variável de incremento até o último caractere, que pode ser qualquer coisa [$((PS2c=0))-9]. Dessa forma, garantimos que não emitimos o valor, e ainda assim o atribuímos. É muito legal - eu nunca fiz isso antes. Mas o POSIX também nos garante que esta é a maneira mais portátil de fazer isso.
E é graças ao POSIX especificado ${parameter} $((expansion))que mantém essas definições no shell atual sem exigir que as definamos em um subshell separado, independentemente de onde as avaliamos. E é por isso que funciona em dashe shtão bem quanto em bashe zsh. Não usamos escapes dependentes do shell / terminal e permitimos que as variáveis se testem. É isso que torna o código portátil rápido.
O resto é bastante simples - basta incrementar nosso contador para cada vez que $PS2é avaliado até $PS1que o redefina novamente. Como isso:
PS2='$((PS2c=PS2c+1)) > '
Então agora eu posso:
DASH DEMO
ENV=/tmp/prompt dash -i
/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cat <<HERE
1 > line 1
2 > line 2
3 > line $((PS2c-1))
4 > HERE
line 1
line 2
line 3
/u/s/m/man3 > printf '\t%s\n' "$PS1" "$PS2" "$PS2c"
$(ps1)${PS2c##*[$((PS2c=0))-9]}
$((PS2c=PS2c+1)) >
0
/u/s/m/man3 > cd ~
/h/mikeserv >
SH DEMO
Funciona da mesma forma em bashou sh:
ENV=/tmp/prompt sh -i
/h/mikeserv > cat <<HEREDOC
1 > $( echo $PS2c )
2 > $( echo $PS1 )
3 > $( echo $PS2 )
4 > HEREDOC
4
$(ps1)${PS2c##*[$((PS2c=0))-9]}
$((PS2c=PS2c+1)) >
/h/mikeserv > echo $PS2c ; cd /
0
/ > cd /usr/share
/u/share > cd ~
/h/mikeserv > exit
Como eu disse acima, o principal problema é que você precisa considerar onde faz o cálculo. Você não obtém o estado no shell pai - portanto, não computa lá. Você obtém o estado no subshell - e é aí que você calcula. Mas você faz a definição no shell pai.
ENV=/dev/fd/3 sh -i 3<<\PROMPT
ps1() { printf '$((PS2c=0)) > ' ; }
ps2() { printf '$((PS2c=PS2c+1)) > ' ; }
PS1=$(ps1)
PS2=$(ps2)
PROMPT
0 > cat <<MULTI_LINE
1 > $(echo this will be line 1)
2 > $(echo and this line 2)
3 > $(echo here is line 3)
4 > MULTI_LINE
this will be line 1
and this line 2
here is line 3
0 >
man 1 mktemp.