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$PS2c0
Maseugosteidisso.Eeuqueriademonstrarosfundamentosdoquefazestetrabalhoumpoucomelhor.Entãoeuediteiissoumpouco.Euenfieiem/tmp
porenquanto,masachoquevouguardarparamimtambém.Estáaqui:
cat/tmp/prompt
SCRIPTDEPROMPT:
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
$PS1
is evaluated it parses and prints$PWD
to add to the prompt. But I don't like the whole$PWD
crowding 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
$PWD
and the most reliable way to do that is with$IFS
split 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
$PWD
on/
symbols. I also use parameter expansion to assign to$last
everything 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$last
will still equal the whole$PWD
and$1
will be empty. This matters. I also strip$last
from the tail end of$PWD
before assigning it to$@
.
printf "${1+%c/}" "$@"
So here - as long as
${1+is set}
weprintf
the first%c
haracter 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$PWD
but the top one. It's important though to realize this only happens if$1
gets set at all, which will not happen at root/
or at one removed from/
such as in/etc
.
printf "$last > "
$last
is 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
$PS2
conditional. 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 \b
ackspaces 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 >