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 /tmp
por 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 %c
string - 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 $PWD
para adicionar ao prompt. Mas eu não gosto da $PWD
multidã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 $PWD
e a maneira mais confiável de fazer isso é com a $IFS
divisã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 $PWD
em /
símbolos. Também uso a expansão de parâmetros para atribuir a $last
tudo após qualquer valor entre a /
barra mais à esquerda e mais à direita . Dessa forma, eu sei que se eu estiver apenas com /
apenas um /
, $last
ainda será igual ao todo $PWD
e $1
ficará vazio. Isso importa. Também retiro $last
o final da cauda $PWD
antes de atribuí-lo $@
.
printf "${1+%c/}" "$@"
Então aqui - contanto ${1+is set}
que seja printf
o primeiro a %c
caracterizar 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, $PWD
exceto o principal. É importante, no entanto, perceber que isso só acontece se $1
for 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 $PS2
condicional. 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 \b
espaç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 dash
e sh
tão bem quanto em bash
e 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é $PS1
que 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 bash
ou 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
.