
Quiero escribir un PROMPT_COMMAND
archivo que responda a lo que se proporcionó en el símbolo del sistema inmediatamente anterior. Por ejemplo, para cambiar entre un mensaje informativo amplio o un mensaje simple y compacto, así:
mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $
Los valores solo parecen agregarse al historial del shell si no están vacíos, por lo que no puedo simplemente probar si la entrada más reciente del historial está en blanco. Ejecutar set | grep some_command
tras ejecutar some_command
no me da nada, por lo que no parece que haya una variable de entorno que contenga esa información.
Principalmente uso bash
, pero tendría curiosidad sobre las soluciones compatibles con POSIX y otros shells.
Respuesta1
Al final no necesitaba PROMPT_COMMAND
nada. Gracias a Christopher por indicarme la dirección correcta.
En su lugar, considere este archivo ps1.prompt
:
${__cmdnbary[\#]+$(
echo '\u@\h: \w' # Your fancy prompt goes here, with all the usual special characters available.
) }${__cmdnbary[\#]=}\$
Luego puedo introducir esto en mi PS1
:
PS1=$(cat ps1.prompt)
(No es necesario hacerlo de esta manera, pero lo encontré conveniente para ilustración y edición).
Y así 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 el truco de matriz.demostrado aquí, pero en lugar de usar bash
la sustitución de ${parameter:-word}
parámetros, usamos ${parameter+word}
so que activamos solo si no se ha ejecutado ningún comando previamente.
Esto requiere alguna explicación, ya que nos vemos obligados a utilizar una doble negativa en nuestra lógica.
¿Cómo ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
funciona?
En la demostración original del hack de matriz, ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
se utilizó la construcción. (Lo he reemplazado $?
por word
para mayor claridad). Si no está particularmente familiarizado con la expansión de parámetros y las matrices (yo no lo estaba), no está del todo claro lo que está sucediendo.
primero, entender\#
Porel manual:
\#
el número de comando de este comando
...
El número de comando es la posición en la secuencia de comandos ejecutados durante la sesión de shell actual.
Esto significa que \#
sólo cambiarási y solo sise ejecuta un comando. Si el usuario ingresa una línea en blanco cuando se le solicita, no se ejecuta ningún comando y, por lo tanto, \#
no cambiará.
La configuración de una cadena vacía en ${__cmdnbary[#]=}
${__cmdnbary[\#]=}
utiliza expansión de parámetros. Volviendo ael manual:
${parameter:=word}
Asignar valores predeterminados. Si el parámetro no está configurado o es nulo, la expansión de la palabra se asigna al parámetro. Luego se sustituye el valor del parámetro.
Entonces, si __cmdnbary[\#]
no está configurado o es nulo, esta construcción asignará una cadena vacía ( word
es una cadena vacía en nuestro caso) y toda la construcción será reemplazada en nuestra salida con esa misma cadena vacía.
__cmdnbary[\#]
voluntadsiempreno estar configurado o ser nulo la primera vez que lo veamos, ya que # es monótono: siempre aumenta o permanece igual. (Es decir, hasta que se repite, probablemente alrededor de 2^31 o 2^63, pero hay otros problemas que tendremoslargoantes de que lleguemos allí. Hay una razón por la que describo la solución como un truco).
El condicional en${__cmdnbary[\#]-word}
${__cmdnbary[\#]-word}
es otra expansión de parámetros. Del manual:
${parameter:-word}
Usar valores predeterminados. Si el parámetro no está establecido o es nulo, se sustituye la expansión de la palabra. De lo contrario, se sustituye el valor del parámetro.
Entonces, si la entrada de la matriz en \#
esno configurado o nulo, word
se acostumbra en su lugar. Dado que no intentamos asignar __cmdnbary[\#]
(usando la ${parameter:=word}
sustitución) hasta después de verificarlo, la primera vez que lo verifiquemos para un determinado \#
debería encontrar esa posición en la matriz sin establecer.
bash
utiliza matrices dispersas
Un punto de aclaración para quienes estén acostumbrados a los arreglos estilo C. bash
realmente usamatrices dispersas; hasta que asignasalgoa una posición en una matriz, esa posición no está establecida. Una cadena vacía no es lo mismo que "nula o no configurada".
Por qué usamos ${__cmdnbary[#]+word}${__cmdnbary[#]=} en su lugar
${__cmdnbary[\#]+word}${__cmdnbary[\#]=}
y ${__cmdnbary[#]-palabra}${__cmdnbary[#]=} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use
${parámetro:+palabra} instead of
${parámetro:-palabra}`.
Recuerde que con ${parameter:-word}
, word
se presenta solo si parameter
es nulo o no está establecido; en nuestro caso, solo sino lo he hechoestablezca la posición en la matriz todavía, lo cual no habremos hecho si y solo si \#
se ha incrementado, lo que solo sucederá si acabamos de ejecutar un comando.
Eso significa que, con ${parameter:-word}
, solo presentaremos word
sino lo he hechoejecutamos un comando, que es precisamente lo contrario de lo que queremos hacer. Entonces, usamos ${parameter:-word}
en su lugar. Nuevamente, del manual:
${parameter:+word}
Usar valor alternativo. Si el parámetro es nulo o no está establecido, no se sustituye nada; de lo contrario, se sustituye la expansión de la palabra.
Lo cual (desafortunadamente) es más lógica doblemente negativa de entender, pero ahí lo tienes.
El mensaje en sí
Hemos explicado el mecanismo de cambio, pero ¿qué pasa con el mensaje en sí?
Aquí, suelo $( ... )
contener el meollo del mensaje. Principalmente para mi propio beneficio de legibilidad; no tienes que hacerlo de esa manera. Puede reemplazarlo $( ... )
con cualquier cosa que normalmente incluya en una asignación de variable.
¿Por qué es un truco?
¿Recuerdas cómo agregamos entradas a una matriz dispersa? No eliminaremos esas entradas, por lo que la matriz crecerá para siempre hasta que se cierre la sesión del shell; el caparazón se está filtrando PS1
. Y hasta donde yo sé, no hay manera dedesarmadouna posición de variable o matriz en el mensaje. Podrías intentarlo $()
, pero descubrirás que no funcionará; Los cambios realizados en el espacio de nombres de la variable dentro de un subshell no se aplicarán al espacio desde el que se bifurcó el subshell.
Túpodríaintente utilizar información mktmp
al principio de la tarea .bashrc
, antes de PS1
la tarea y demás información en el archivo resultante; entonces podría comparar su corriente \#
con lo que ha almacenado allí, pero ahora ha hecho que su aviso dependa de la E/S del disco, lo cual es una buena manera de bloquearse en situaciones de emergencia.