bash - programas - scripts linux ejercicios resueltos
¿Cómo detectar si mi shell script se está ejecutando a través de una tubería? (5)
El comando test
(incorporado en bash
), tiene una opción para verificar si un descriptor de archivo es un tty.
if [ -t 1 ]; then
# stdout is a tty
fi
Consulte " man test
" o " man bash
" y busque " -t
"
¿Cómo detecto desde un script de shell si su salida estándar se envía a un terminal o si se canaliza a otro proceso?
El caso en cuestión: me gustaría agregar códigos de escape para colorear la salida, pero solo cuando se ejecuta de manera interactiva, pero no cuando se canaliza, de manera similar a lo que hace ls --color
.
En Solaris, la sugerencia de Dejay Clayton funciona principalmente. El -p no responde como se desea.
bash_redir_test.sh se ve como:
[[ -t 1 ]] && /
echo ''STDOUT is attached to TTY''
[[ -p /dev/stdout ]] && /
echo ''STDOUT is attached to a pipe''
[[ ! -t 1 && ! -p /dev/stdout ]] && /
echo ''STDOUT is attached to a redirection''
En Linux, funciona muy bien:
:$ ./bash_redir_test.sh
STDOUT is attached to TTY
:$ ./bash_redir_test.sh | xargs echo
STDOUT is attached to a pipe
:$ rm bash_redir_test.log
:$ ./bash_redir_test.sh >> bash_redir_test.log
:$ tail bash_redir_test.log
STDOUT is attached to a redirection
En Solaris:
:# ./bash_redir_test.sh
STDOUT is attached to TTY
:# ./bash_redir_test.sh | xargs echo
STDOUT is attached to a redirection
:# rm bash_redir_test.log
bash_redir_test.log: No such file or directory
:# ./bash_redir_test.sh >> bash_redir_test.log
:# tail bash_redir_test.log
STDOUT is attached to a redirection
:#
En un shell POSIX puro,
if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi
devuelve "terminal", porque la salida se envía a su terminal, mientras que
(if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi) | cat
devuelve "no es un terminal", porque la salida de la paréntesis se canaliza a cat
.
La bandera -t
se describe en las páginas man como
-t fd Verdadero si el descriptor de archivos fd está abierto y se refiere a un terminal.
... donde fd
puede ser una de las asignaciones habituales de descriptores de archivos:
0: stdin
1: stdout
2: stderr
No hay una manera infalible de determinar si STDIN, STDOUT o STDERR se canalizan a / desde su script, principalmente debido a programas como ssh
.
Cosas que "normalmente" funcionan
Por ejemplo, la siguiente solución de bash funciona correctamente en un shell interactivo:
[[ -t 1 ]] && /
echo ''STDOUT is attached to TTY''
[[ -p /dev/stdout ]] && /
echo ''STDOUT is attached to a pipe''
[[ ! -t 1 && ! -p /dev/stdout ]] && /
echo ''STDOUT is attached to a redirection''
Pero no siempre funcionan.
Sin embargo, cuando se ejecuta este comando como un comando ssh
no es TTY, las secuencias STD siempre parecen estar siendo canalizadas. Para demostrar esto, usa STDIN porque es más fácil:
# CORRECT: Forced-tty mode correctly reports ''1'', which represents
# no pipe.
ssh -t localhost ''[[ -p /dev/stdin ]]; echo ${?}''
# CORRECT: Issuing a piped command in forced-tty mode correctly
# reports ''0'', which represents a pipe.
ssh -t localhost ''echo hi | [[ -p /dev/stdin ]]; echo ${?}''
# INCORRECT: Non-tty mode reports ''0'', which represents a pipe,
# even though one isn''t specified here.
ssh -T localhost ''[[ -p /dev/stdin ]]; echo ${?}''
Por que importa
Esto es bastante importante, porque implica que no hay forma de que un script bash diga si un comando ssh
no-tty está siendo canalizado o no. Tenga en cuenta que este comportamiento desafortunado se introdujo cuando las versiones recientes de ssh
comenzaron a utilizar tuberías para STDIO que no eran TTY. Las versiones anteriores utilizaban sockets, que PODRÍAN diferenciarse de dentro de bash utilizando [[ -S ]]
.
Cuando importa
Esta limitación normalmente causa problemas cuando se desea escribir una secuencia de comandos bash que tenga un comportamiento similar al de una utilidad compilada, como cat
. Por ejemplo, cat
permite el siguiente comportamiento flexible en el manejo de varias fuentes de entrada simultáneamente, y es lo suficientemente inteligente como para determinar si está recibiendo una entrada canalizada independientemente de si se está utilizando ssh
no TTY o TTY forzado:
ssh -t localhost ''echo piped | cat - <( echo substituted )''
ssh -T localhost ''echo piped | cat - <( echo substituted )''
Solo puede hacer algo así si puede determinar de manera confiable si las tuberías están involucradas o no. De lo contrario, la ejecución de un comando que lee STDIN cuando no hay entrada disponible desde los conductos o la redirección hará que el script se bloquee y espere la entrada de STDIN.
Otras cosas que no funcionan.
Al tratar de resolver este problema, he observado varias técnicas que no logran resolver el problema, incluidas las que involucran:
- examinando las variables del entorno SSH
- usando
stat
en los descriptores de archivo / dev / stdin - examinando el modo interactivo a través de
[[ "${-}" =~ ''i'' ]]
- examinando el estado de tty vía
tty
ytty -s
- examinando el estado de
ssh
través de[[ "$(ps -o comm= -p $PPID)" =~ ''sshd'' ]]
Tenga en cuenta que si está utilizando un sistema operativo que admita el sistema de archivos virtual /proc
, es posible que tenga suerte al seguir los enlaces simbólicos de STDIO para determinar si se está utilizando una canalización o no. Sin embargo, /proc
no es una solución multiplataforma compatible con POSIX.
Estoy extremadamente interesado en resolver este problema, así que avíseme si piensa en alguna otra técnica que pueda funcionar, preferiblemente en soluciones basadas en POSIX que funcionen tanto en Linux como en BSD.
No menciona qué shell está utilizando, pero en Bash, puede hacer esto:
#!/bin/bash
if [[ -t 1 ]]; then
# stdout is a terminal
else
# stdout is not a terminal
fi