¿Para qué sirve la variable $ BASH_COMMAND?

25

Según el manual de Bash , la variable de entorno BASH_COMMANDcontiene

El comando que se está ejecutando actualmente o que se va a ejecutar, a menos que el shell esté ejecutando un comando como resultado de una captura, en cuyo caso es el comando que se ejecuta en el momento de la captura.

Si entiendo el caso de la esquina de la trampa, si entiendo correctamente, esto significa que cuando ejecuto un comando, la variable BASH_COMMANDcontiene ese comando. No está absolutamente claro si esa variable se desarma después de la ejecución del comando (es decir, solo está disponible mientras el comando se está ejecutando, pero no después), aunque se podría argumentar que dado que es "el comando que se está ejecutando actualmente o está por ejecutarse" , no es el comando que era solo ejecutar.

Pero revisemos:

$ set | grep BASH_COMMAND=
$ 

Vacío. Hubiera esperado ver BASH_COMMAND='set | grep BASH_COMMAND='o tal vez soloBASH_COMMAND='set' , pero vacío me sorprendió.

Probemos algo más:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

Bueno, eso tiene sentido. Ejecuto el comando echo $BASH_COMMANDy la variable BASH_COMMANDcontiene la cadena echo $BASH_COMMAND. ¿Por qué funcionó esta vez, pero no antes?

Hagamos la setcosa otra vez:

$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

Entonces espera. Que se establece cuando ejecuté que echocomando, y no fue desactivada después. Pero cuando ejecuté setnuevamente, BASH_COMMAND no estaba configurado para el setcomando. No importa con qué frecuencia ejecute el setcomando aquí, el resultado sigue siendo el mismo. Entonces, ¿se establece la variable al ejecutar echo, pero no al ejecutar set? Veamos.

$ echo Hello AskUbuntu
Hello AskUbuntu
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

¿Qué? Entonces, ¿la variable se configuró cuando ejecuté echo $BASH_COMMAND, pero no cuando ejecuté echo Hello AskUbuntu? ¿Dónde está la diferencia ahora? ¿La variable solo se establece cuando el comando actual en sí mismo obliga al shell a evaluar la variable? Probemos algo diferente. Tal vez algún comando externo esta vez, no un bash incorporado, para variar.

$ /bin/echo $BASH_COMMAND
/bin/echo $BASH_COMMAND
$ set | grep BASH_COMMAND=
BASH_COMMAND='/bin/echo $BASH_COMMAND'
$

Hmm, ok ... otra vez, la variable fue establecida. Entonces, ¿es correcta mi suposición actual? ¿La variable solo se establece cuando debe evaluarse? ¿Por qué? ¿Por qué? Por razones de rendimiento? Hagamos un intento más. Intentaremos grep $BASH_COMMANDen un archivo, y como $BASH_COMMANDdebería contener un grepcomando, grepdebería grep para ese grepcomando (es decir, para sí mismo). así que hagamos un archivo apropiado:

$ echo -e "1 foo\n2 grep\n3 bar\n4 grep \$BASH_COMMAND tmp" > tmp
$ grep $BASH_COMMAND tmp
grep: $BASH_COMMAND: No such file or directory
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
$ set | grep BASH_COMMAND=
BASH_COMMAND='grep --color=auto $BASH_COMMAND tmp'
$

Ok, interesante El comando grep $BASH_COMMAND tmpse expandió a grep grep $BASH_COMMAND tmp tmp(la variable se expande solo una vez, por supuesto), por lo que busqué grep, una vez en un archivo $BASH_COMMANDque no existe, y dos veces en el archivo tmp.

P1: ¿Es correcto mi supuesto actual de que:

  • BASH_COMMANDsolo se establece cuando un comando intenta evaluarlo realmente; y
  • se no desactívala después de la ejecución de un comando, a pesar de que la descripción que nos puede llevar a creer eso?

P2: En caso afirmativo, ¿por qué? ¿Actuación? Si no, ¿de qué otra manera se puede explicar el comportamiento en la secuencia de comandos anterior?

P3: Por último, ¿hay algún escenario en el que esta variable realmente pueda usarse de manera significativa? En realidad estaba tratando de usarlo $PROMPT_COMMANDpara analizar el comando que se estaba ejecutando (y hacer algunas cosas dependiendo de eso), pero no puedo, porque tan pronto como, dentro de mi $PROMPT_COMMAND, ejecuto un comando para mirar la variable $BASH_COMMAND, la variable obtiene conjuntos para ese comando. Incluso cuando lo hago MYVARIABLE=$BASH_COMMANDal comienzo de mi $PROMPT_COMMAND, luego MYVARIABLEcontiene la cadena MYVARIABLE=$BASH_COMMAND, porque una asignación también es un comando. (Esta pregunta no se trata de cómo podría obtener el comando actual dentro de una $PROMPT_COMMANDejecución. Hay otras formas, lo sé).

Es un poco como con el principio de incertidumbre de Heisenberg. Simplemente observando la variable, la cambio.

Malte Skoruppa
fuente
55
Agradable, pero probablemente más adecuado para unix.stackexchange.com ... hay verdaderos bashüber-gurus allí.
Rmano
Bueno, ¿es posible moverlo allí? Mods?
Malte Skoruppa
Realmente no estoy seguro de cómo hacerlo --- supongo que es un privilegio Mod. Por otro lado, no creo que marcarlo sea sensato --- veamos si alguien actúa en la bandera cercana.
Rmano
1
Para el tercer punto, esto es lo que estás buscando. Quizás en base a ese artículo, también puede encontrar la respuesta para los primeros dos puntos.
Radu Rădeanu
2
¡+1 me confundió muchísimo, pero fue divertido de leer!
Joe

Respuestas:

15

Respondiendo a la tercera pregunta: por supuesto, se puede usar de manera significativa en la forma en que el manual de Bash sugiere claramente, en una trampa, por ejemplo:

$ trap 'echo ‘$BASH_COMMAND’ failed with error code $?' ERR
$ fgfdjsa
fgfdjsa: command not found
fgfdjsa failed with error code 127
$ cat /etc/fgfdjsa
cat: /etc/fgfdjsa: No such file or directory
cat /etc/fgfdjsa failed with error code 1
Dmitry Alexandrov
fuente
Ah, bien hecho. Entonces, ¿diría que una trampa es el único contexto en el que esta variable tiene sentido? Sonó como un caso de esquina en el manual: "El comando que se ejecuta (...), a menos que el shell esté ejecutando un comando como resultado de una trampa, ..."
Malte Skoruppa
Después de leer los artículos vinculados en la respuesta de @DocSalvager, está claro que de $BASH_COMMANDhecho se puede usar de manera significativa incluso en contextos distintos de una trampa. Sin embargo, el uso que describe es probablemente el más típico y directo. Entonces, su respuesta, y la de DocSalvager, responde mi Q3 mejor, y esta fue la más importante para mí. En cuanto a Q1 y Q2, como lo indica @zwets, esas cosas no están definidas. Es posible que $BASH_COMMANDsolo se le dé un valor si se accede a él, para el rendimiento, o bien algunas condiciones extrañas del programa interno pueden conducir a ese comportamiento. ¿Quién sabe?
Malte Skoruppa
1
En una nota al margen, descubrí que puede forzar $BASH_COMMANDque se configure siempre, forzando la evaluación $BASH_COMMANDantes de cada comando. Para hacer esto, podemos usar la trampa DEBUG, que se ejecuta antes de cada comando (todos ellos). Si emitimos, por ejemplo, trap 'echo "$BASH_COMMAND" > /dev/null' DEBUGy luego $BASH_COMMANDsiempre será evaluado antes de cada comando (y asignado el valor de esa $COMMAND). Entonces, si ejecuto set | grep BASH_COMMAND=mientras esa trampa está activa ... ¿qué sabes? Ahora la salida es BASH_COMMAND=set, como se esperaba :)
Malte Skoruppa
6

Ahora que Q3 ha sido respondido (correctamente, en mi opinión: BASH_COMMANDes útil en trampas y casi en ningún otro lado), demos una oportunidad a Q1 y Q2.

La respuesta a Q1 es: la exactitud de su suposición es indecidible. No se puede establecer la verdad de ninguno de los puntos, ya que preguntan sobre el comportamiento no especificado. Por su especificación, el valor de BASH_COMMANDse establece en el texto de un comando durante la ejecución de ese comando. La especificación no indica cuál debe ser su valor en ninguna otra situación, es decir, cuando no se ejecuta ningún comando. Podría tener cualquier valor o ninguno en absoluto.

La respuesta a la P2 "Si no, ¿de qué otra manera se puede explicar el comportamiento en la secuencia de comandos anterior?" luego sigue lógicamente (si es algo pedante): se explica por el hecho de que el valor de BASH_COMMANDno está definido. Como su valor no está definido, puede tener cualquier valor, que es exactamente lo que muestra la secuencia.

Posdata

Hay un punto en el que creo que realmente estás llegando a un punto débil en la especificación. Es donde dices:

Incluso cuando hago MYVARIABLE = $ BASH_COMMAND justo al comienzo de mi $ PROMPT_COMMAND, MYVARIABLE contiene la cadena MYVARIABLE = $ BASH_COMMAND, porque una asignación también es un comando .

La forma en que leo la página de manual de bash, el bit en cursiva no es cierto. La sección SIMPLE COMMAND EXPANSIONexplica cómo primero se dejan de lado las asignaciones de variables en la línea de comando, y luego

Si no se obtiene ningún nombre de comando [en otras palabras, solo hubo asignaciones variables], las asignaciones variables afectan el entorno actual del shell.

Esto me sugiere que las asignaciones de variables no son comandos (y, por lo tanto, están exentos de aparecer en BASH_COMMAND), como en otros lenguajes de programación. Esto también explicaría por qué no hay una línea BASH_COMMAND=seten la salida de set,set siendo esencialmente un 'marcador' sintáctico para la asignación de variables.

OTOH, en el párrafo final de esa sección dice

Si queda un nombre de comando después de la expansión, la ejecución continúa como se describe a continuación. De lo contrario, el comando sale.

... lo que sugiere lo contrario, y las asignaciones variables también son comandos.

zwets
fuente
1
El hecho de que no se pueda establecer alguna suposición no significa que sea incorrecta. Einstein asumió que la velocidad de la luz es la misma para cualquier observador (a cualquier velocidad) como una hipótesis fundamental para la teoría de la relatividad; eso no se puede establecer, pero esto no significa que sea incorrecto. Las propiedades "se pueden establecer" y "es correcta" son ortogonales. En el mejor de los casos, podría decir "No sabemos si es correcto, ya que no se puede establecer". Además, se especifica: es el comando actual durante la ejecución. Entonces, si escribo set, debería ver BASH_COMMAND='set'en algún lugar de esa salida, que
Malte Skoruppa
... sin embargo, no es el caso. Aunque eso es durante la ejecución del setcomando . Por lo tanto, de acuerdo con las especificaciones, debería funcionar. Entonces, ¿es que la implementación no obedece fielmente las especificaciones, o no entiendo las especificaciones? Encontrar la respuesta a esa pregunta es una especie de propósito de Q1. +1 para tu postdata :)
Malte Skoruppa
@MalteSkoruppa Buenos puntos. Se arregló la respuesta en consecuencia y se agregó un comentario sobre set.
zwets
1
Es posible que en el intérprete bash, setno sea un "comando". Así como =no es un "comando". El procesamiento del texto que sigue / rodea esos tokens podría tener una lógica completamente diferente que el procesamiento de un "comando".
DocSalvager
Buenos puntos de los dos. :) Bien puede ser que setno cuente como un "comando". No hubiera pensado que $BASH_COMMANDresultaría tan poco especificado en muchas circunstancias. Supongo que al menos hemos establecido que la página del manual definitivamente podría ser más clara al respecto;)
Malte Skoruppa
2

Un uso inventivo para $ BASH_COMMAND

Recientemente encontré este uso impresionante de $ BASH_COMMAND en la implementación de una funcionalidad tipo macro.

Este es el truco central del alias y reemplaza el uso de la trampa DEBUG. Si lees la parte en la publicación anterior sobre la trampa DEBUG, reconocerás la variable $ BASH_COMMAND. En esa publicación, dije que estaba configurado con el texto del comando antes de cada llamada a la trampa DEBUG. Bueno, resulta que está configurado antes de la ejecución de cada comando, DEBUG trap o no (por ejemplo, ejecute 'echo "este comando = $ BASH_COMMAND"' para ver de qué estoy hablando). Al asignarle una variable (solo para esa línea) capturamos BASH_COMMAND en el alcance más externo del comando, que contendrá el comando completo.

El artículo anterior del autor también proporciona algunos buenos antecedentes al implementar una técnica utilizando un DEBUG trap. El trapse elimina en la versión mejorada.

DocSalvager
fuente
+1 Finalmente pude leer esos dos artículos. ¡Guauu! ¡Qué lectura! Muy esclarecedor ¡Ese tipo es un bash guru ! ¡Prestigio! Esto también responde a mi comentario sobre la respuesta de Dmitry sobre si $BASH_COMMANDse puede usar de manera significativa en un contexto que no sea a trap. ¡Y qué uso es eso! Utilizándolo para implementar comandos de macro en bash: ¿quién lo hubiera pensado posible? Gracias por el puntero.
Malte Skoruppa
0

Un uso aparentemente común para la trampa ... depuración es mejorar los títulos que aparecen en la lista de ventanas (^ A ") cuando está usando" pantalla ".

Estaba tratando de hacer que la "pantalla", "lista de ventanas" fuera más utilizable, así que comencé a encontrar artículos que hacen referencia a "trampa ... depuración".

Descubrí que el método que usa PROMPT_COMMAND para enviar la "secuencia de título nulo" no funcionaba tan bien, así que volví al método trap ... debug.

Así es como lo haces:

1 Indique a "pantalla" que busque la secuencia de escape (habilítela) colocando "shelltitle '$ | bash:'" en "$ HOME / .screenrc".

2 Desactive la propagación de la trampa de depuración en subcapas. Esto es importante, porque si está habilitado, todo se arruina, use: "set + o functrace".

3 Envíe la secuencia de escape del título para "pantalla" para interpretar: trap 'printf "\ ek $ (fecha +% Y% m% d% H% M% S) $ (whoami) @ $ (nombre de host): $ (pwd) $ {BASH_COMMAND} \ e \ "'" DEBUG "

No es perfecto, pero ayuda, y puedes usar este método para poner literalmente todo lo que quieras en el título

Consulte la siguiente "lista de ventanas" (5 pantallas):

Num Nombre Banderas

1 bash: 20161115232035 mcb @ ken007: / home / mcb / ken007 RSYNCCMD = "sudo rsync" myrsync.sh -R -r "$ {1}" "$ {BACKUPDIR} $ {2: +" / $ {2} " } "$ 2 bash: 20161115230434 mcb @ ken007: / home / mcb / ken007 ls --color = auto -la $ 3 bash: 20161115230504 mcb @ ken007: / home / mcb / ken007 cat bin / psg.sh $ 4 bash: 20161115222415 mcb @ ken007: / home / mcb / ken007 ssh ken009 $ 5 bash: 20161115222450 mcb @ ken007: / home / mcb / ken007 mycommoncleanup $

Malcolm Boekhoff
fuente