Wie kann ein Bash-Skript feststellen, wie es ausgeführt wurde?

10

Ich habe ein Bash-Skript, das ich erstellen wollte, um einen ziemlich komplexen Befehl mit kleinen Änderungen auszuführen, nach denen ich durch Echo und Lesen gefragt werde.

Ich habe Lösungen gefunden, um es zu zwingen, ein Terminal auszuführen, um den Befehl auszuführen, aber das interessiert mich nicht. Was ich tun möchte, ist, wenn ich in Nautilus die Leertaste drücke und einfach die Eingabetaste drücke (damit es mit Run Software ausgeführt wird), wird nur eine Benachrichtigung mit der Meldung "Bitte führen Sie dies von einem Terminal aus."

Ich kann das Popup aktivieren - wie ich den Befehl kenne -, aber ich kann das Bash-Skript nicht dazu bringen, festzustellen, ob es in einem Terminal ausgeführt wird oder nicht, es scheint immer so zu sein. Ist es überhaupt möglich?

Aescula
quelle

Antworten:

10

Aus man bashunter BEDINGTE AUSDRÜCKE :

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

Angenommen, fd 1 ist Standard, if [ -t 1 ]; thensollte für Sie funktionieren. Das Advanced Shell Scripting Guide behauptet, dass -tdiese Methode ein Failover darstellt sshund dass der Test (mit stdin, nicht stdout) daher wie folgt lauten sollte:

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

-pTestet, ob eine Datei vorhanden ist und eine Named Pipe ist. Allerdings würde ich, beachten Sie erfahrungsgemäß das für mich nicht wahr ist: -p /dev/stdinnicht für beide normalen Terminals und SSH - Sitzungen während if [ -t 0 ](oder -t 1) in beiden Fällen funktioniert (siehe auch Gilles Kommentaren unten zu Themen in diesem Abschnitt der Advanced Shell Scripting Guide ).


Wenn das Hauptproblem ein spezialisierter Kontext ist, aus dem Sie das Skript aufrufen möchten, um sich in einem für diesen Kontext geeigneten Verhalten zu verhalten, können Sie all diese technischen Details umgehen und sich mit einem Wrapper und einer benutzerdefinierten Variablen ein wenig Aufwand ersparen:

!#/bin/bash

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

Nennen Sie dies live_script.shoder was auch immer und doppelklicken Sie stattdessen darauf. Sie können natürlich dasselbe mit Befehlszeilenargumenten erreichen, aber es wäre immer noch ein Wrapper erforderlich, damit Point and Click in einem GUI-Dateibrowser funktioniert.

Goldlöckchen
quelle
5
Dies ist die richtige Antwort - so sagt POSIX auch, dass eine Shell erkennen sollte, ob sie interaktiv ist oder nicht.
Mikesserv
2
@DanielAmaya - Wenn Sie Eingaben umleiten, wird das Skript nicht auf einem Terminal ausgeführt. Die Frage ist, wie festgestellt werden kann, ob das Skript auf einem Terminal ausgeführt wird.
Mikesserv
2
Sind Sie sicher über die Verwendung von ||in der [ … ]Art? Wenn Sie [[ … ]]dann verwenden, wäre es in Ordnung, aber normalerweise wird das ||verwendet, um Befehle zu trennen, und [ -t 0ist ein falscher Aufruf von, [weil sein letzter ]fehlt. Normalerweise gibt es auch keinen Befehl -p. Ich bin mit dem Testen eines Terminals einverstanden. das ist wahrscheinlich der Weg, es zu tun. Es ist nur die Syntax, um die ich mir Sorgen mache.
Jonathan Leffler
1
@ JonathanLeffler Richtig; Dies sollte einen Syntaxfehler erzeugen, da der Shell-Operator ||vor dem erforderlichen endgültigen ]Argument für angezeigt wird [.
Chepner
3
Dieser Abschnitt aus dem Advanced Bash-Scripting Guide enthält mehrere Fehler. PS1ist kein zuverlässiger Test, um festzustellen, ob die Shell interaktiv ist. "Wenn ein Skript testen muss, ob es in einer interaktiven Shell ausgeführt wird", ist ebenfalls verwirrend: Es sollte sein, wenn Code getestet werden muss - ein Skript wird normalerweise nicht in einer interaktiven Shell ausgeführt (kann es aber sein, wenn es aus Quellen stammt). . Das Testen auf iin $-ist der richtige Weg, um zu testen, ob die Shell interaktiv ist. Testen -t 0oder -t 2ist der richtige Weg, um festzustellen, ob das Skript in einem Terminal ausgeführt wird, das sich von der interaktiven unterscheidet.
Gilles 'SO - hör auf böse zu sein'
0

Verwenden Sie die Variable bash $ SHLVL, um die Ebene der Shell-Verschachtelung zu ermitteln. In einem Skript, das 'raw' durch Doppelklicken ausführt, ist es 1, in einem Skript, das in einem Terminal ausgeführt wird, ist es 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
quelle
0

Obwohl die Antwort von Goldlöckchen im typischen Fall wahrscheinlich richtig ist, scheint es Randfälle zu geben. In meinem Fall ist mein xserver für den Start konfiguriert tty1und verlässt diesen tty nie. Wenn Xorgs stdoutein TTY ist, scheint es, dass die Clients dieses TTY standardmäßig mit ihrem Dateideskriptor verknüpft haben.

So habe ich mein Problem gelöst:

#!/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

Ich habe dies nicht getestet, um festzustellen, ob es mit einer Standard-X-Konfiguration funktioniert, und ich bezweifle auch sehr, dass dies der einzige Randfall ist. Wenn jemand eine allgemeinere Lösung findet, kommen Sie bitte zurück und teilen Sie uns dies mit.

JMW
quelle
-2

Eine andere, die die Bash-Optionen verwendet, setzt die interne Variable $-.

Von .bashrc,

# If not running interactively, don't do anything
case $- in
    *i*) ;;
    *) return;;
esac
xae
quelle
Eine interaktive Shell ist nicht unbedingt mit einem Terminal verbunden. Während eine mit dieser Verbindung gestartete Verbindung automatisch interaktiv gestartet wird, ist dies auch möglich : cmd | sh -i | cmd.
Mikeserv
Dieser Code wird in einem Skript ausgeführt. Es ist nicht interaktiv, selbst wenn es in einem Terminal ausgeführt wird.
Gilles 'SO - hör auf böse zu sein'