Em última análise, não precisei de PROMPT_COMMAND
. Obrigado a Christopher por me apontar na direção certa.
Em vez disso, considere este arquivo, ps1.prompt
:
${__cmdnbary[\#]+$(
echo '\u@\h: \w' # Your fancy prompt goes here, with all the usual special characters available.
) }${__cmdnbary[\#]=}\$
Eu posso alimentar isso no meu PS1
:
PS1=$(cat ps1.prompt)
(Você não precisa fazer isso dessa maneira, mas achei conveniente para ilustração e edição.)
E assim vemos:
mikemol@zoe:~$ echo hi
hi
mikemol@zoe:~$ echo ho
ho
mikemol@zoe:~$ echo hum
hum
mikemol@zoe:~$
mikemol@zoe:~$ PS1=$(cat ps1.prompt)
$
mikemol@zoe: ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@zoe: ~ $
Estamos usando o hack da matriz demonstrado aqui , mas em vez de usar o parâmetro bash
${parameter:-word}
substituição, usamos ${parameter+word}
, então acionamos somente quando não houve execução de comando anterior.
Isso requer alguma explicação, pois somos forçados a usar um negativo duplo em nossa lógica.
Como o ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
funciona
Na demonstração de hack do array original, o constructo ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
foi usado. (Eu substituí $?
com word
para maior clareza). Se você não está particularmente familiarizado com a expansão de parâmetros e matrizes (não estava), não está claro o que está acontecendo.
Primeiro, entenda \#
Por o manual :
\#
the command number of this command
...
The command number is the position in the sequence of commands executed during the current shell session.
Isso significa que \#
somente mudará se e somente se um comando for executado. Se o usuário digitar uma linha em branco no prompt, nenhum comando será executado e, portanto, \#
não será alterado.
A configuração de uma string vazia em $ {__ cmdnbary [#] =}
${__cmdnbary[\#]=}
usa expansão de parâmetro. Voltando ao manual :
${parameter:=word}
Assign Default Values. If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted.
Portanto, se __cmdnbary[\#]
não for definido ou nulo, essa construção atribuirá uma string vazia ( word
é uma string vazia em nosso caso) e toda a construção será substituída em nossa saída com a mesma string vazia. / p>
__cmdnbary[\#]
sempre será indefinido ou nulo na primeira vez que o virmos, pois # é monotônico - sempre incrementa ou permanece o mesmo. (Isto é, até o loop, provavelmente em torno de 2 ^ 31 ou 2 ^ 63, mas há outros problemas que teremos tempo antes de chegarmos lá. Há uma razão para descrever a solução como um pouco de um hack.)
A condicional em ${__cmdnbary[\#]-word}
${__cmdnbary[\#]-word}
é outra expansão de parâmetro. Do manual:
${parameter:-word}
Use Default Values. If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
Assim, se a entrada da matriz em \#
for não definida ou nula , word
será usado em seu lugar. Como não tentamos atribuir a __cmdnbary[\#]
(usando a substituição ${parameter:=word}
) até depois de verificá-la, a primeira vez que verificamos uma determinada \#
deve encontrar essa posição na matriz não definida.
bash
usa matrizes esparsas
Um ponto de esclarecimento para aqueles acostumados a arrays no estilo C. bash
na verdade usa matrizes esparsas ; até você atribuir algo a uma posição em uma matriz, essa posição não é definida. Uma string vazia não é a mesma coisa que "null ou unset".
Por que usamos $ {__ cmdnbary [#] + word} $ {__ cmdnbary [#] =} em vez
${__cmdnbary[\#]+word}${__cmdnbary[\#]=}
e $ {__ cmdnbary [#] - palavra} $ {__ cmdnbary [#] =} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use
$ {parâmetro: + palavra} instead of
$ {parameter: -word} '.
Lembre-se de que com ${parameter:-word}
, word
é apresentado apenas se parameter
for nulo ou não definido - no nosso caso, somente se não tivermos definido a posição na matriz ainda , o que não teremos feito se e somente se \#
tiver incrementado, o que só acontecerá se tivermos acabado de executar um comando.
Isso significa que, com ${parameter:-word}
, apresentaremos apenas word
se não tivermos executado um comando, que é exatamente o oposto do que queremos fazer. Então, usamos ${parameter:-word}
. Mais uma vez, no manual:
${parameter:+word}
Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.
Qual é (infelizmente), mais lógica negativa dupla para entender, mas aí está você.
O próprio prompt
Nós explicamos o mecanismo de switching, mas e o prompt em si?
Aqui, eu uso $( ... )
para conter a carne do prompt. Principalmente para meu próprio benefício por legibilidade; você não precisa fazer isso dessa maneira. Você pode substituir $( ... )
por qualquer coisa que normalmente possa ser incluída em uma atribuição de variável.
Por que é um hack?
Lembre-se de como estamos adicionando entradas a um array esparso? Não estamos removendo essas entradas e, portanto, a matriz crescerá para sempre até que a sessão do shell seja encerrada; o shell está vazando por PS1
. E até onde eu sei, não há como desassociar uma variável ou posição de array no prompt. Você poderia tentar em $()
, mas descobrirá que não funcionará; as alterações feitas no namespace da variável dentro de um subshell não serão aplicadas ao espaço em que a subshell foi bifurcada.
Você pode tentar usar mktmp
no início de sua atribuição .bashrc
, antes de PS1
, e informações sobre o material no arquivo resultante; então você poderia comparar seu atual \#
com o que você armazenou lá, mas agora você tornou seu prompt dependente de E / S de disco, o que é uma boa maneira de se trancar em situações de emergência.