Por ejemplo, línea de comando:
test.sh arg1 | grep "xyz"
¿Es posible obtener la línea de comando completa que incluye el siguiente grep en el script bash test.sh?
[ -t 1 ] unix.stackexchange.com/a/401938/70524
Por ejemplo, línea de comando:
test.sh arg1 | grep "xyz"
¿Es posible obtener la línea de comando completa que incluye el siguiente grep en el script bash test.sh?
[ -t 1 ] unix.stackexchange.com/a/401938/70524
Respuestas:
No hay forma de hacerlo en general .
Pero un bashshell interactivo puede aprovechar el mecanismo del historial y la DEBUGtrampa para "decir" a los comandos que ejecuta la línea de comandos completa de la que forman parte a través de una variable de entorno:
$ trap 'export LC=$(fc -nl -0); LC=${LC#? }' DEBUG
$ sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true
last_command={sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true}
No
bash (o su shell) bifurcará dos comandos distintos.
test.sh arg1grep "xyz"test.sh No podría saber acerca de seguir grep.
Sin embargo, puede saber que está "dentro" de una tubería al probar /proc/self/fd/1
test.sh
#!/bin/bash
file /proc/self/fd/1
que corren como
> ./test.sh
/proc/self/fd/1: symbolic link to /dev/pts/0
> ./test.sh | cat
/proc/self/fd/1: broken symbolic link to pipe:[25544239]
(Editar) vea el comentario de muru sobre saber si está en una tubería.
no necesitas saber si estás en una tubería para eso. Simplemente verifique si la salida es un TTY.
[ -t 1 ]https://unix.stackexchange.com/a/401938/70524
Al usar /proc/self/fd, puede ver si está en una tubería, así como una ID para la tubería. Si itera /proc/\*/fdbuscando la tubería coincidente, puede encontrar el PID del otro extremo de la tubería. Con el PID, puede leer /proc/$PID/cmdliney repetir el proceso en sus descriptores de archivo para encontrar en qué se canaliza.
$ cat | cat | cat &
$ ps
PID TTY TIME CMD
6942 pts/16 00:00:00 cat
6943 pts/16 00:00:00 cat
6944 pts/16 00:00:00 cat
7201 pts/16 00:00:00 ps
20925 pts/16 00:00:00 bash
$ ls -l /proc/6942/fd
lrwx------. 1 tim tim 64 Jul 24 19:59 0 -> /dev/pts/16
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581130]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6943/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581130]'
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6944/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 1 -> /dev/pts/16
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
Además, si tiene suerte, los diferentes comandos en la tubería obtendrán PID consecutivos que lo harán un poco más fácil.
En realidad no tengo un guión para hacer esto, pero he demostrado el concepto.
Otra forma podría ser acceder a la $BASH_COMMANDvariable automática, pero es inherentemente volátil y difícil de atrapar el valor deseado.
Creo que solo puedes atraparlo solo a través de un eval, lo que también implica invocar tus líneas de comando de una manera especial, como en:
CMD="${BASH_COMMAND##* eval }" eval './test.sh arg1 | grep "xyz"'
Aquí $BASH_COMMANDse expande al mismo tiempo que se purga hasta el evalbit de cadena, y la cadena de resultado se "captura" en una $CMDvariable auxiliar .
Pequeño ejemplo:
$ cat test.sh
#!/bin/sh
printf 'you are running %s\n' "$CMD"
sleep 1
echo bye bye
$
$ CMD="${BASH_COMMAND##* eval }" eval './test.sh | { grep -nH "."; }'
(standard input):1:you are running './test.sh | { grep -nH "."; }'
(standard input):2:bye bye
$
Naturalmente, también puede funcionar (en realidad mejor) al invocar scripts por ej. sh -cO bash -c, como en:
$
$ CMD="${BASH_COMMAND}" sh -c './test.sh | { grep -nH "."; }'
(standard input):1:you are running CMD="${BASH_COMMAND}" sh -c './test.sh | { grep -nH "."; }'
(standard input):2:bye bye
$
Aquí sin purgar la variable.
Gracias por tus respuestas. He probado diferentes cosas y llegué al siguiente script de prueba:
test.sh:
hist=`fc -nl -0`
# remove leading and trailing whitespaces
hist="$(echo "${hist}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
echo "Command line from history: '$hist'"
if [ -t 1 ]; then
echo "Direct output to TTY, no pipe involved."
else
echo "No TTY, maybe a piped command."
fi
if [ -p /dev/stdout ]; then
echo "stdout is a pipe."
else
echo "stdout is not a pipe."
fi
readlink -e /proc/self/fd/1
rst=$?
if [ $rst -eq 0 ]; then
echo "Readlink test status okay, no pipe involved."
else
echo "Readlink test status error $rst, maybe a piped command."
fi
Pruebas:
$ ./test.sh test1
Command line from history: './test.sh test1'
Direct output to TTY, no pipe involved.
stdout is not a pipe.
/dev/pts/3
Readlink test status okay, no pipe involved.
$ ./test.sh test2 | cat
Command line from history: './test.sh test2 | cat'
No TTY, maybe a piped command.
stdout is a pipe.
Readlink test status error 1, maybe a piped command.
$ echo "another command before pipe doesn't matter" | ./test.sh test3
Command line from history: 'echo "another command before pipe doesn't matter" | ./test.sh test3'
Direct output to TTY, no pipe involved.
stdout is not a pipe.
/dev/pts/3
Readlink test status okay, no pipe involved.
El historial de la línea de comandos solo funciona sin un Shebang en la línea superior del script. No sé si esto funcionará de manera confiable y también en otros sistemas.
No pude suprimir la salida de "readlink" (o "archivo" como se sugiere desde Archemar), cuando el estado fue exitoso ("/ dev / pts / 3"). La salida de tubería a / dev / null o a una variable conduciría a un mal funcionamiento. Entonces, esta no sería una opción para mí en un script.
La verificación de TTY que mencionó muru es fácil y tal vez ya sea suficiente para algunos casos de uso.
Editar: Mi crédito es para mosvy, porque la pregunta era cómo obtener la línea de comando completa y no solo para determinar si el script está en curso. Me gusta la parte simple "fc -nl -0" en su respuesta, porque no se necesita más configuración del sistema. No es una solución al 100 por ciento, pero es solo para mi uso personal y, por lo tanto, suficiente. Gracias a todos los demás por vuestra ayuda.
[ -t 0 ]. Por lo tanto, puede verificar si stdin o stdout no es un TTY y proceder en consecuencia.
if [ -p /dev/stdout ]; ...(así como readlink /proc/self/fd/..esto no funciona en BSD).
echo -ecasi seguro que no quiere el -e. Necesita más casos de prueba, redirigir a un archivo, ser invocado dentro $(...). Sin embargo, le recomiendo que considere si es una buena idea. Los programas como los lsque cambian su salida dependiendo de si están saliendo a un tty o una tubería son molestos de usar.