¿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 forma interactiva, pero no cuando se canaliza, de forma similar a lo que ls --color
hace.
Respuestas:
En un shell POSIX puro,
devuelve "terminal", porque la salida se envía a su terminal, mientras que
devuelve "no un terminal", porque la salida del paréntesis se canaliza a
cat
.La
-t
bandera se describe en las páginas del manual como... donde
fd
puede ser una de las asignaciones de descriptor de archivo habituales:fuente
-t
indicador se especifica en POSIX y, por lo tanto, debería funcionar para cualquier shell compatible con POSIX (es decir, no es una extensión bash). pubs.opengroup.org/onlinepubs/009695399/utilities/test.htmlfish
respuesta de shell. El usotest
es ordenado, pero no puedo probar el ejemplo entre paréntesis, ya que no es compatible. Intenté envolverlo de manera análogabegin; ...; end
, pero eso no pareció funcionar, y simplemente ejecutó el bloque de código positivo nuevamente. Pensé que podría necesitar usar,status
pero eso no parece comprobar si hay tuberías. Supongo que esencialmente quiero comprobar si STDOUT de un comando / script anterior no está configurado en el terminal, gracias a estas aclaraciones.No hay una forma infalible de determinar si STDIN, STDOUT o STDERR se están canalizando hacia / desde su script, principalmente debido a programas como
ssh
.Cosas que "normalmente" funcionan
Por ejemplo, la siguiente solución bash funciona correctamente en un shell interactivo:
Pero no siempre funcionan
Sin embargo, al ejecutar este comando como un comando que no es TTY
ssh
, las transmisiones STD siempre parecen estar siendo canalizadas. Para demostrar esto, use STDIN porque es más fácil:Por que es importante
Este es un gran problema, porque implica que no hay forma de que un script bash sepa si un
ssh
comando que no es tty se está canalizando o no. Tenga en cuenta que este comportamiento desafortunado se introdujo cuando las versiones recientes dessh
comenzaron a utilizar tuberías para STDIO no TTY. Las versiones anteriores usaban sockets, que PODRÍAN diferenciarse desde dentro de bash usando[[ -S ]]
.Cuando importa
Esta limitación normalmente causa problemas cuando desea escribir un script bash que tenga un comportamiento similar a 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 sissh
se está utilizando TTY no forzado o TTY forzado :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 cualquiera de las tuberías o la redirección dará como resultado que el script se cuelgue y espere la entrada de STDIN.
Otras cosas que no funcionan
Al tratar de resolver este problema, he examinado varias técnicas que no logran resolverlo, incluidas las que incluyen:
stat
descriptores de archivo on / dev / stdin[[ "${-}" =~ 'i' ]]
tty
ytty -s
ssh
estado a través de[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
Tenga en cuenta que si está utilizando un sistema operativo que admite el
/proc
sistema de archivos virtual, es posible que tenga suerte siguiendo los enlaces simbólicos de STDIO para determinar si se está utilizando una tubería o no. Sin embargo,/proc
no es una solución multiplataforma compatible con POSIX.Soy extremadamente interesante para resolver este problema, así que avíseme si piensa en alguna otra técnica que pueda funcionar, preferiblemente soluciones basadas en POSIX que funcionen tanto en Linux como en BSD.
fuente
stat
llamada en / dev / stdin. ¿Y por qué funciona"${-}"
otty -s
no? También busqué en el código fuente decat
pero no veo qué parte está haciendo la magia allí que no puedes hacer en el shell POSIX. ¿Puede explicar más?El comando
test
(incorporadobash
) tiene una opción para verificar si un descriptor de archivo es un tty.Ver "
man test
" o "man bash
" y buscar "-t
"fuente
help test
(yhelp help
para más), luegoinfo bash
para obtener información más detallada. Estos comandos son excelentes si alguna vez terminas haciendo scripts fuera de línea, o si solo quieres obtener una comprensión más amplia.No mencionas qué shell estás usando, pero en Bash, puedes hacer esto:
fuente
En Solaris, la sugerencia de Dejay Clayton funciona principalmente. La -p no responde como se desea.
bash_redir_test.sh se parece a:
En Linux, funciona muy bien:
En Solaris:
fuente
El siguiente código (probado solo en linux bash 4.4) no debe considerarse portátil ni recomendado , pero en aras de la exhaustividad aquí es:
No sé por qué, pero parece que el descriptor de archivo "3" se crea de alguna manera cuando una función bash tiene STDIN canalizado.
Espero eso ayude,
fuente