Comment vérifier si mon shell fonctionne sur un terminal?

22

Je veux effectuer une action uniquement si mon shell est "connecté" à un terminal, c'est-à-dire uniquement si mon entrée standard provient de l'entrée d'un terminal et que ma sortie standard (et l'erreur standard? Peut-être que cela n'a pas d'importance) est imprimée / répercutée sur un terminal.

Comment puis-je faire cela, sans compter /proc/selfdirectement sur les spécificités GNU / Linux (comme )?

einpoklum - réintégrer Monica
la source
Similaire: unix.stackexchange.com/q/22162/117549
Jeff Schaller

Réponses:

33

isattyest une fonction pour vérifier cela , et le -tdrapeau de la testcommande le rend accessible à partir d'un script shell:

-t descripteur_fichier

Vrai si le numéro de descripteur de fichier file_descriptor est ouvert et est associé à un terminal. False si file_descriptor n'est pas un numéro de descripteur de fichier valide, ou si le numéro de descripteur de fichier file_descriptor n'est pas ouvert, ou s'il est ouvert mais n'est pas associé à un terminal.

Vous pouvez vérifier si FD 0 (entrée standard) est un ATS avec:

test -t 0

Vous pouvez faire de même pour les FD 1 et 2 pour vérifier les flux de sortie et d'erreur, ou tous:

test -t 0 -a -t 1 -a -t 2

La commande retourne 0 (réussit) si les descripteurs sont connectés à un terminal et est fausse sinon.

testest également disponible comme [commande pour un "test de parenthèse":

 if [ -t 0 ] ; then ...

est une façon idiomatique d'écrire ce conditionnel.

Michael Homer
la source
8

J'imagine que c'est un doublon, mais je ne le trouve pas. Utilisation

[ -t 0 ]

et

[ -t 1 ]

pour tester respectivement si l'entrée et la sortie standard sont connectées à une borne. man testa les détails.

Stephen Kitt
la source
7

Juste une note supplémentaire en plus des bonnes réponses qui ont déjà été données. Notez que [ -t 0 ]teste que le descripteur de fichier 0 est ouvert dans un fichier qui est un fichier de périphérique avec une discipline de ligne tty (généralement, cela se fait en vérifiant qu'un termio (s) inoffensif ioctl () réussit).

En outre, cela ne signifie pas nécessairement qu'il existe un terminal ou un émulateur de terminal (avec un véritable utilisateur tapant sur un clavier) à l'autre extrémité (bien que dans l'immense majorité des cas et probablement la plupart de ceux qui vous intéressent, c'est assez bon pour approximation).

Les dispositifs tty et pty peuvent également être utilisés pour le transfert de données ou comme mécanisme de communication interprocessus.

Par exemple, on pourrait faire:

(stty raw -echo; myscript) < /dev/ttyS0

Pour alimenter ce qui est reçu via RS232 myscript.

echo test | ssh -tt host myscript

aurait myscriptstdin étant un périphérique pty (avec sshdà l'autre extrémité, et finalement (à travers la connexion ssh) pas un terminal, mais un tuyau alimenté par echo)

Pour vérifier en outre qu'il y a un terminal à l'autre extrémité de cette ligne RS232 ou pty, vous pouvez également vérifier qu'une $TERMvariable est définie et non vide ( [ -n "$TERM" ]) et envoyer une séquence d'échappement de rapport d'état du périphérique sur ce fd et vérifier que vous recevez une réponse (en plus du [ -t 0 ]et [ -n "$TERM" ]).

printf >&0 '\e[5n'

Est répondu \e[0npar la plupart des terminaux.

Maintenant, il y a plusieurs problèmes avec cela, donc je ne recommanderais pas de le faire, sauf dans le cas où vous voulez vérifier cela parce que vous voulez exécuter une application TUI visuelle (dans ce cas, vous feriez mieux d'utiliser des bibliothèques comme ncurses, et au lieu du DSR, vous préférez envoyer une séquence d'échappement d'identification de périphérique pour interroger le type de terminal plus précisément que via $TERM):

  • Heureusement, dans la plupart des cas où stdin n'est pas un terminal, il aura été ouvert en mode lecture seule, ce qui entraînerait l' printféchec, mais si stdin est un périphérique tty ouvert en mode lecture + écriture, cela aura l'effet secondaire d'envoyer cette séquence à l'autre extrémité. Par exemple, dans notre exemple ssh ci-dessus, cela enverra en fait la séquence à un terminal (mais la réponse ne viendra pas sur stdin)
  • Il est difficile de lire la réponse de manière fiable et portable. Vous devez modifier temporairement la discipline de ligne tty et lire un octet à la fois. Vous devrez également décider d'un délai d'expiration auquel, si la réponse n'est pas vue, vous abandonnez et décidez qu'il n'y a pas de terminal. Si vous voulez que les gens se connectent par satellite, cela signifie un long délai.
  • La lecture à partir d'un terminal en arrière-plan suspendrait votre script avec un signal SIGTTIN.
Stéphane Chazelas
la source