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.