
Paraobteromesmoresultadoquevocêanotanasuapergunta,tudooqueénecessárioéoseguinte:
PS1='${PS2c##*[$((PS2c=0))-9]}->'PS2='$((PS2c=PS2c+1))>'Vocênãoprecisasecontorcer.EssasduaslinhasfarãotudoemqualquershellquepretendaalgopróximoàcompatibilidadePOSIX.
->cat<<HD1>line12>line$((PS2c-1))3>HDline1line2->echo$PS2c0Maseugosteidisso.Eeuqueriademonstrarosfundamentosdoquefazestetrabalhoumpoucomelhor.Entãoeuediteiissoumpouco.Euenfieiem/tmpporenquanto,masachoquevouguardarparamimtambém.Estáaqui:
cat/tmp/promptSCRIPTDEPROMPT:
ps1(){IFS=/set--${PWD%"${last=${PWD##/*/}}"}
printf "${1+%c/}" "$@"
printf "$last > "
}
PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'
PS2='$((PS2c=PS2c+1)) > '
Observação: tendo recentemente aprendido sobre yash , eu o construí ontem. Por alguma razão, ele não imprime o primeiro byte de cada argumento com a string %c - embora os documentos sejam específicos sobre extensões wide-char para esse formato e talvez estejam relacionados - mas funciona bem com %.1s
Essa é a coisa toda. Há duas coisas principais acontecendo lá em cima. E é assim que 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 >
PARSING $PWD
Every time
$PS1is evaluated it parses and prints$PWDto add to the prompt. But I don't like the whole$PWDcrowding my screen, so I want just the first letter of every breadcrumb in the current path down to the current directory, which I'd like to see in full. Like this:
/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cd /
/ > cd ~
/h/mikeserv >
There are a few steps here:
IFS=/we're going to have to split the current
$PWDand the most reliable way to do that is with$IFSsplit on/. No need to bother with it at all afterward - all splitting from here on out will be defined by the shell's positional parameter$@array in the next command like:
set -- ${PWD%"${last=${PWD##/*/}}"}So this one's a little tricky, but the main thing is that we're splitting
$PWDon/symbols. I also use parameter expansion to assign to$lasteverything after any value occurring between the left-most and right-most/slash. In this way I know that if I'm just at/and have only one/then$lastwill still equal the whole$PWDand$1will be empty. This matters. I also strip$lastfrom the tail end of$PWDbefore assigning it to$@.
printf "${1+%c/}" "$@"So here - as long as
${1+is set}weprintfthe first%character of each our shell's arguments - which we've just set to each directory in our current$PWD- less the top directory - split on/. So we're essentially just printing the first character of every directory in$PWDbut the top one. It's important though to realize this only happens if$1gets set at all, which will not happen at root/or at one removed from/such as in/etc.
printf "$last > "
$lastis the variable I just assigned to our top directory. So now this is our top directory. It prints whether or not the last statement did. And it takes a neat little>for good measure.
MAS O QUE É SOBRE O INCREMENTO?
And then there's the matter of the
$PS2conditional. I showed earlier how this can be done which you can still find below - this is fundamentally an issue of scope. But there's a little more to it unless you want to start doing a bunch ofprintf \backspaces and then trying to balance out their character count... ugh. So I do this:
PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'Again,
${parameter##expansion}saves the day. It's a little strange here though - we actually set the variable while we strip it of itself. We use its new value - set mid-strip - as the glob from which we strip. You see? We##*strip all from the head of our increment variable to the last character which can be anything from[$((PS2c=0))-9]. We're guaranteed in this way not to output the value, and yet we still assign it. It's pretty cool - I've never done that before. But POSIX also guarantees us that this is the most portable way this can be done.
E é graças ao ${parameter} $((expansion)) especificado pelo POSIX 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 de shell / terminal e deixamos as variáveis testarem a si mesmas. Isso é o que torna o código portátil rápido.
O restante é bastante simples - basta incrementar nosso contador para cada vez que $PS2 for avaliado até que $PS1 redefina novamente. Assim:
PS2='$((PS2c=PS2c+1)) > '
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 você faz o seu cálculo. Você não obtém o estado no shell pai - assim você não calcula lá. Você obtém o estado na subcamada - 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 >