Comment un script Bash peut-il dire comment il a été exécuté?

10

J'ai un script Bash que j'essayais de faire pour m'aider à exécuter une commande plutôt complexe avec de petites modifications qu'il me poserait par écho et lirait.

J'ai trouvé des solutions pour le forcer à exécuter un terminal pour exécuter la commande, mais cela ne m'intéresse pas. Ce que j'aimerais, c'est que si j'espace et que je tape simplement sur Entrée dans Nautilus (en le faisant fonctionner avec Run Software), il affichera simplement une notification disant "Veuillez exécuter ceci à partir d'un terminal".

Je peux faire apparaître le popup - comme je connais la commande - mais je ne peux pas obtenir le script Bash pour dire s'il est exécuté à l'intérieur d'un terminal ou non, il semble toujours le penser. Est-ce même possible?

Aescula
la source

Réponses:

10

De man bashsous EXPRESSIONS CONDITIONNELLES :

-t fd  
    True if file descriptor fd is open and refers to a terminal.

En supposant que fd 1 est une sortie standard, cela if [ -t 1 ]; thendevrait fonctionner pour vous. Le Advanced Script Scripting Guide affirme que -tcette méthode échouera sshet que le test (en utilisant stdin, pas stdout) devrait donc être:

if [[ -t 0 || -p /dev/stdin ]]

-pteste si un fichier existe et est un canal nommé. Cependant , je noterais par expérience que ce n'est pas vrai pour moi: -p /dev/stdinéchoue à la fois pour les terminaux normaux et les sessions ssh alors que if [ -t 0 ](ou -t 1) fonctionne dans les deux cas (voir également les commentaires de Gilles ci-dessous sur les problèmes dans cette section du Guide de script shell avancé ).


Si le problème principal est un contexte spécialisé à partir duquel vous souhaitez appeler le script pour se comporter d'une manière appropriée à ce contexte, vous pouvez contourner toutes ces technicités et vous épargner quelques tracas en utilisant un wrapper et une variable personnalisée:

!#/bin/bash

export SPECIAL_CONTEXT=1
/path/to/real/script.sh

Appelez ceci live_script.shou n'importe quoi et double-cliquez à la place. Vous pouvez bien sûr accomplir la même chose avec des arguments de ligne de commande, mais un wrapper serait toujours nécessaire pour faire pointer et cliquer dans un navigateur de fichiers GUI.

boucle d'or
la source
5
c'est la bonne réponse - c'est aussi la façon dont POSIX dit qu'un shell doit détecter s'il est interactif ou non.
mikeserv
2
@DanielAmaya - si vous redirigez l'entrée, le script n'est pas exécuté sur un terminal. La question est de savoir comment détecter si le script est exécuté sur un terminal.
mikeserv
2
Êtes-vous sûr de l'utilisation de l' ||intérieur [ … ]comme ça? Si vous l'utilisez, [[ … ]]ce serait bien, mais normalement, le ||est utilisé pour séparer les commandes et il [ -t 0s'agit d'une invocation incorrecte [car sa dernière ]est manquante. Il n'y a généralement pas de commande -pnon plus. Je suis d'accord pour tester un terminal; c'est probablement la façon de procéder. C'est juste la syntaxe qui m'inquiète.
Jonathan Leffler
1
@JonathanLeffler Right; cela devrait produire une erreur de syntaxe, puisque l'opérateur shell ||est vu avant l' ]argument final requis pour [.
chepner
3
Cette section du Guide de script Bash avancé contient plusieurs erreurs. PS1n'est pas un test fiable pour savoir si le shell est interactif. «Si un script doit tester s'il s'exécute dans un shell interactif» est également déroutant: cela devrait être le cas si du code doit être testé - un script ne s'exécute généralement pas dans un shell interactif (mais il peut l'être, s'il provient) . Test pour ien $-est la bonne manière de vérifier si le shell est interactif. Tester -t 0ou -t 2est la bonne façon de savoir si le script s'exécute dans un terminal, ce qui est différent d'être interactif.
Gilles 'SO- arrête d'être méchant'
0

Utilisez la variable bash $ SHLVL pour détecter le niveau d'imbrication du shell. Dans un script exécuté `` brut '' en double-cliquant, ce sera 1, dans un script exécuté dans un terminal, ce sera 2.

#!/bin/bash
if (( SHLVL < 2 )) ; then
    echo "Please run this from a terminal."
    read -p "Press <Enter> to close this window"
    exit 1
fi
# rest of script
Ed Randall
la source
0

Bien que la réponse de goldilocks soit probablement correcte dans le cas typique, il semble qu'il y ait des cas de bord. Dans mon propre cas, mon xserver est configuré pour démarrer tty1et il ne quitte jamais ce tty. Si Xorg stdoutest un TTY, il semble que les clients auront ce TTY lié à leur descripteur de fichier par défaut.

Voici comment j'ai résolu mon problème:

#!/bin/bash
isxclient=$( readlink /dev/fd/2 | grep -q 'tty' && [[ -n $DISPLAY ]] ; echo $? )
if [[ ! -t 2  || $isxclient == "0" ]]; then
        notify-send "Script wasn't started from an interactive shell"
else
        echo "Script was started from an interactive shell"
fi

Je n'ai pas testé cela pour voir si cela fonctionne sur une configuration X plus standard, et je doute également beaucoup que ce soit le seul cas de bord. Si quelqu'un trouve une solution plus généralement applicable, veuillez revenir et nous le dire.

JMW
la source
-2

Une autre, en utilisant les options de bash variable initialisée interne, $-.

De .bashrc,

# If not running interactively, don't do anything
case $- in
    *i*) ;;
    *) return;;
esac
xae
la source
un shell interactif n'est pas nécessairement connecté à un terminal. alors on a commencé avec cette connexion est lancée automatiquement interactive, ceci est également possible: cmd | sh -i | cmd.
mikeserv
Ce code est exécuté dans un script. Il ne sera pas interactif, même s'il s'exécute dans un terminal.
Gilles 'SO- arrête d'être méchant'