
Quero escrever um PROMPT_COMMAND
que responda ao que foi fornecido no prompt de comando imediatamente anterior. Por exemplo, para alternar entre um prompt expansivo e informativo ou um prompt simples e compacto, como este:
mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $
Os valores só parecem ser adicionados ao histórico do shell se não estiverem vazios, portanto, não posso simplesmente testar se a entrada mais recente do histórico está em branco. Correr set | grep some_command
após correr some_command
não me dá nada, então não parece que haja uma variável de ambiente contendo essas informações.
Eu uso principalmente bash
, mas ficaria curioso sobre soluções compatíveis com POSIX e outros shells.
Responder1
No final das contas, eu não precisava PROMPT_COMMAND
de nada. 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[\#]=}\$
Posso então alimentar isso em meu PS1
:
PS1=$(cat ps1.prompt)
(Você não precisa fazer desta forma, 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 de arraydemonstrado aqui, mas em vez de usar a substituição de parâmetro bash
de ${parameter:-word}
, usamos ${parameter+word}
assim, acionamos apenas se não houver execução de comando anterior.
Isso requer alguma explicação, pois somos forçados a usar um duplo negativo em nossa lógica.
Como ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
funciona
Na demonstração original do array hack, a construção ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
foi usada. (substituí $?
por word
para maior clareza). Se você não está particularmente familiarizado com expansão de parâmetros e matrizes (eu não estava), não está claro o que está acontecendo.
Primeiro, entenda\#
Poro manual:
\#
o número do comando deste comando
...
O número do comando é a posição na sequência de comandos executados durante a sessão atual do shell.
Isso significa \#
que só mudaráse e apenas seum comando é executado. Se o usuário inserir 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âmetros. Retornando parao manual:
${parameter:=word}
Atribuir valores padrão. Se o parâmetro não estiver definido ou for nulo, a expansão da palavra será atribuída ao parâmetro. O valor do parâmetro é então substituído.
Portanto, se __cmdnbary[\#]
não estiver definido ou for nulo, esta construção atribuirá uma string vazia ( word
é uma string vazia no nosso caso) e toda a construção será substituída em nossa saída pela mesma string vazia.
__cmdnbary[\#]
vaisempreserá unset ou nulo na primeira vez que o virmos, já que # é monotônico - sempre aumenta ou permanece o mesmo. (Isto é, até que ele faça um loop, provavelmente em torno de 2 ^ 31 ou 2 ^ 63, mas há outros problemas que teremoslongoantes de chegarmos lá. Há uma razão pela qual descrevo a solução como um hack.)
O condicional em${__cmdnbary[\#]-word}
${__cmdnbary[\#]-word}
é outra expansão de parâmetro. Do manual:
${parameter:-word}
Usar valores padrão. Se o parâmetro não estiver definido ou for nulo, a expansão da palavra será substituída. Caso contrário, o valor do parâmetro é substituído.
Então, se a entrada da matriz em \#
fornão definido ou nulo, word
é usado em seu lugar. Como não tentamos atribuir a __cmdnbary[\#]
(usando a ${parameter:=word}
substituição) antes de verificá-lo, a primeira vez que verificarmos um determinado \#
deve encontrar essa posição no array não definida.
bash
usa matrizes esparsas
Um ponto de esclarecimento para quem está acostumado com arrays estilo C. bash
realmente usamatrizes esparsas; até você atribuiralgopara uma posição em uma matriz, essa posição não será definida. Uma string vazia não é a mesma coisa que "nulo ou não definido".
Por que usamos ${__cmdnbary[#]+word}${__cmdnbary[#]=}
${__cmdnbary[\#]+word}${__cmdnbary[\#]=}
e ${__cmdnbary[#]-word}${__cmdnbary[#]=} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use
${parameter:+word} instead of
${parameter:-word}`.
Lembre-se de que with ${parameter:-word}
, word
é apresentado apenas se parameter
for nulo ou não definido - no nosso caso, apenas senão tenhodefinir a posição no array ainda, o que não teremos feito se e somente se \#
tiver incrementado, o que só acontecerá se apenas executarmos um comando.
Isso significa que, com ${parameter:-word}
, só apresentaremos word
senão tenhoexecutou um comando, que é exatamente o oposto do que queremos fazer. Então, usamos ${parameter:-word}
em vez disso. Novamente, do manual:
${parameter:+word}
Usar valor alternativo. Se o parâmetro for nulo ou não definido, nada será substituído, caso contrário, a expansão da palavra será substituída.
O que é (infelizmente) mais lógica duplamente negativa para entender, mas aí está você.
O prompt em si
Explicamos o mecanismo de comutação, mas e o prompt em si?
Aqui, eu uso $( ... )
para conter a essência do prompt. Principalmente para meu próprio benefício para facilitar a leitura; você não precisa fazer assim. Você pode substituir $( ... )
por qualquer coisa que normalmente colocaria em uma atribuição de variável.
Por que é um hack?
Lembra como adicionamos entradas a um array esparso? Não estamos removendo essas entradas e, portanto, o array crescerá para sempre até que a sessão do shell seja encerrada; a casca está vazando PS1
. E até onde eu sei, não há comodesarmaruma variável ou posição de array no prompt. Você poderia tentar $()
, 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 de onde o subshell foi bifurcado.
Vocêpodertente usar mktmp
no início da sua tarefa .bashrc
, antes PS1
da tarefa, e coloque as informações no arquivo resultante; então você poderia comparar sua corrente \#
com o que você armazenou lá, mas agora você tornou seu prompt dependente da E/S do disco, o que é uma boa maneira de se bloquear em situações de emergência.