Comment détecter à partir d'un script shell si sa sortie standard est envoyée à un terminal ou si elle est dirigée vers un autre processus?
Le cas d'espèce: je voudrais ajouter des codes d'échappement pour coloriser la sortie, mais uniquement lorsqu'il est exécuté de manière interactive, mais pas lorsqu'il est canalisé, comme c'est le ls --color
cas.
Réponses:
Dans un shell POSIX pur,
renvoie "terminal", car la sortie est envoyée à votre terminal, alors que
renvoie "pas un terminal", car la sortie de la parenthèse est dirigée vers
cat
.Le
-t
drapeau est décrit dans les pages de manuel comme... où
fd
peut être l'une des affectations de descripteurs de fichiers habituelles:la source
-t
indicateur est spécifié dans POSIX et devrait donc fonctionner pour tout shell compatible POSIX (c'est-à-dire qu'il ne s'agit pas d'une extension bash). pubs.opengroup.org/onlinepubs/009695399/utilities/test.htmlfish
réponse shell. L'utilisationtest
est soignée, mais je ne peux pas essayer l'exemple entre parenthèses car cela n'est pas pris en charge. J'ai essayé de l'envelopper dans un analoguebegin; ...; end
, mais cela ne semblait pas fonctionner, et a simplement exécuté à nouveau le bloc de code positif. J'ai pensé que je pourrais avoir besoin d'utiliser,status
mais cela ne semble pas vérifier la tuyauterie. Je suppose que je veux essentiellement vérifier si STDOUT d'une commande / script précédente n'est pas définie sur le terminal, grâce à ces réponses de clarification.Il n'existe aucun moyen infaillible de déterminer si STDIN, STDOUT ou STDERR sont dirigés vers / depuis votre script, principalement à cause de programmes comme
ssh
.Choses qui fonctionnent «normalement»
Par exemple, la solution bash suivante fonctionne correctement dans un shell interactif:
Mais ils ne fonctionnent pas toujours
Cependant, lors de l'exécution de cette commande en tant que commande non TTY
ssh
, les flux STD semblent toujours être acheminés. Pour le démontrer, utilisez STDIN car c'est plus simple:Pourquoi est-ce important
C'est un gros problème, car cela implique qu'il n'y a aucun moyen pour un script bash de dire si une
ssh
commande non tty est envoyée ou non. Notez que ce comportement malheureux a été introduit lorsque des versions récentes de ontssh
commencé à utiliser des canaux pour les non-TTY STDIO. Les versions précédentes utilisaient des sockets, qui POURRAIENT être différenciés de bash en utilisant[[ -S ]]
.Quand c'est important
Cette limitation pose normalement des problèmes lorsque vous souhaitez écrire un script bash qui a un comportement similaire à un utilitaire compilé, tel que
cat
. Par exemple,cat
permet le comportement flexible suivant dans la gestion simultanée de diverses sources d'entrée et est suffisamment intelligent pour déterminer s'il reçoit une entrée canalisée, que l'ssh
on utilise ou non un ATS forcé :Vous ne pouvez faire quelque chose comme ça que si vous pouvez déterminer de manière fiable si des tuyaux sont impliqués ou non. Sinon, l'exécution d'une commande qui lit STDIN lorsqu'aucune entrée n'est disponible à partir des canaux ou de la redirection entraîne le blocage du script et l'attente de l'entrée STDIN.
D'autres choses qui ne fonctionnent pas
En essayant de résoudre ce problème, j'ai examiné plusieurs techniques qui ne parviennent pas à résoudre le problème, y compris celles qui impliquent:
stat
de descripteurs de fichiers on / dev / stdin[[ "${-}" =~ 'i' ]]
tty
ettty -s
ssh
statut via[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
Notez que si vous utilisez un système d'exploitation qui prend en charge le
/proc
système de fichiers virtuel, vous aurez peut-être de la chance en suivant les liens symboliques de STDIO pour déterminer si un canal est utilisé ou non. Cependant, il/proc
ne s'agit pas d'une solution multiplateforme compatible POSIX.Je suis extrêmement intéressé à résoudre ce problème, alors faites-moi savoir si vous pensez à une autre technique qui pourrait fonctionner, de préférence des solutions basées sur POSIX qui fonctionnent à la fois sur Linux et BSD.
la source
stat
appel sur / dev / stdin. Et pourquoi ça marche"${-}"
outty -s
pas? J'ai également examiné le code source decat
mais je ne vois pas quelle partie fait la magie que vous ne pouvez pas faire dans le shell POSIX. Pourriez-vous développer ceci?La commande
test
(intégréebash
) a une option pour vérifier si un descripteur de fichier est un tty.Voir "
man test
" ou "man bash
" et rechercher "-t
"la source
help test
(ethelp help
pour plus), puisinfo bash
pour des informations plus détaillées. Ces commandes sont excellentes si vous finissez par créer des scripts hors ligne ou si vous souhaitez simplement obtenir une compréhension plus large.Vous ne mentionnez pas le shell que vous utilisez, mais dans Bash, vous pouvez le faire:
la source
Sur Solaris, la suggestion de Dejay Clayton fonctionne principalement. -P ne répond pas comme souhaité.
bash_redir_test.sh ressemble à:
Sous Linux, cela fonctionne très bien:
Sur Solaris:
la source
Le code suivant (testé uniquement dans linux bash 4.4) ne doit pas être considéré comme portable ni recommandé , mais pour des raisons d'exhaustivité, il est ici:
Je ne sais pas pourquoi, mais il semble que le descripteur de fichier "3" soit en quelque sorte créé quand une fonction bash a canalisé STDIN.
J'espère que ça aide,
la source