Como detectar se um comando foi fornecido para o prompt do shell

4

Eu quero escrever um PROMPT_COMMAND que responda ao que foi fornecido para o prompt de comando imediatamente anterior. Por exemplo, para alternar entre um prompt expansivo e informativo ou um prompt simples e compacto, da seguinte forma:

mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $

Os valores só aparecem para serem adicionados ao histórico do shell se não estiverem vazios, por isso não posso simplesmente testar se a entrada do histórico mais recente está em branco. Executar set | grep some_command depois de executar some_command não me dá nada, por isso não aparece uma variável de ambiente contendo essa informação.

Eu uso principalmente bash , mas ficaria curioso sobre soluções compatíveis com POSIX e outros shells.

    
por Michael Mol 28.04.2017 / 19:21

1 resposta

2

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.

    
por 03.05.2017 / 17:00