W jaki sposób skrypt Bash może powiedzieć, jak został uruchomiony?

10

Mam skrypt Bash, który próbowałem wykonać, aby pomóc mi uruchomić dość złożone polecenie z małymi zmianami, o które zapytałby mnie przez echo i przeczytał.

Znalazłem rozwiązania, które zmuszają go do uruchomienia terminala w celu wykonania polecenia, ale nie jestem tym zainteresowany. Chciałbym to zrobić, jeśli zwolnię miejsce i po prostu naciśniesz Enter na nim w Nautilusie (uruchamiając go z Run Software), po prostu delikatnie wyświetli powiadomienie z informacją: „Uruchom to z terminala”.

Mogę wywołać wyskakujące okienko - tak jak w przypadku polecenia - ale nie mogę uzyskać skryptu Bash z informacją, czy jest uruchamiany w terminalu, czy nie, wydaje się, że zawsze tak jest. Czy to w ogóle możliwe?

Aescula
źródło

Odpowiedzi:

10

Od man bashpod WARUNKOWYCH WYRAŻENIA :

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

Zakładając, że fd 1 jest standardem, if [ -t 1 ]; thenpowinno działać dla ciebie. Te zaawansowane Shell Scripting Guide twierdzi, że -tstosowane w ten sposób nie powiedzie się ssh, a badania (przy użyciu stdin, stdout nie) powinny być:

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

-psprawdza, czy plik istnieje i jest nazwanym potokiem. Jednak zauważam eksperymentalnie, że nie jest to dla mnie prawdą: -p /dev/stdinzawodzi zarówno w przypadku normalnych terminali, jak i sesji ssh, podczas gdy if [ -t 0 ](lub -t 1) działa w obu przypadkach (patrz także komentarze Gilles poniżej na temat problemów w tej sekcji Zaawansowanego Skryptu powłoki ).


Jeśli podstawowym problemem jest wyspecjalizowany kontekst, z którego chcesz wywołać skrypt, aby zachowywał się w sposób odpowiedni dla tego kontekstu, możesz ominąć wszystkie te szczegóły techniczne i zaoszczędzić sobie trochę kłopotu, korzystając z opakowania i zmiennej niestandardowej:

!#/bin/bash

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

Zadzwoń do tego live_script.shlub czegokolwiek i kliknij dwukrotnie zamiast tego. Oczywiście można to osiągnąć za pomocą argumentów wiersza poleceń, ale nadal potrzebne jest opakowanie, aby działała przeglądarka plików GUI.

Złotowłosa
źródło
5
to jest poprawna odpowiedź - w ten sposób POSIX mówi, że powłoka powinna wykryć, czy jest interaktywna, czy nie.
mikeserv
2
@DanielAmaya - jeśli przekierujesz dane wejściowe, skrypt nie jest uruchamiany na terminalu. Pytanie brzmi, jak wykryć, czy skrypt jest uruchamiany na terminalu.
mikeserv
2
Czy jesteś pewien, że użyjesz ||czegoś [ … ]takiego? Jeśli użyjesz [[ … ]], będzie dobrze, ale zwykle ||służy do oddzielania poleceń i [ -t 0jest niepoprawnym wywołaniem, [ponieważ ]brakuje jego ostatniego . Zazwyczaj nie ma też polecenia -p. Zgadzam się z testowaniem terminala; to prawdopodobnie sposób na zrobienie tego. Martwię się tylko o składnię.
Jonathan Leffler
1
@JathanathanLeffler Right; powinno to powodować błąd składniowy, ponieważ operator powłoki ||jest wyświetlany przed wymaganym ostatecznym ]argumentem do [.
chepner
3
Ta sekcja Advanced Bash-Scripting Guide zawiera kilka błędów. PS1nie jest wiarygodnym testem pozwalającym stwierdzić, czy powłoka jest interaktywna. „Jeśli skrypt musi przetestować, czy działa w powłoce interaktywnej”, jest również mylący: powinien być, jeśli jakiś kod wymaga przetestowania - skrypt zwykle nie działa w powłoce interaktywnej (ale może tak być, jeśli jest pozyskiwany) . Testowanie na iin $-jest poprawnym sposobem sprawdzenia, czy powłoka jest interaktywna. Testowanie -t 0lub -t 2poprawny sposób sprawdzenia, czy skrypt działa w terminalu, który różni się od interaktywności.
Gilles „SO- przestań być zły”
0

Użyj zmiennej bash $ SHLVL, aby wykryć poziom zagnieżdżenia powłoki. W skrypcie uruchamianym „surowym” przez dwukrotne kliknięcie będzie to 1, w skrypcie działającym w terminalu będzie to 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
źródło
0

Chociaż odpowiedź goldilocks jest prawdopodobnie poprawna w typowym przypadku, wydaje się, że istnieją przypadki skrajne. W moim przypadku mój Xserver jest skonfigurowany do uruchamiania tty1i nigdy nie pozostawia tego tty. Jeśli Xorg's stdoutjest TTY, wydaje się, że klienci będą domyślnie mieli ten TTY połączony z deskryptorem pliku.

Oto jak rozwiązałem mój problem:

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

Nie testowałem tego, aby sprawdzić, czy działa na bardziej standardowej konfiguracji X, a także bardzo wątpię, czy jest to jedyny przypadek na krawędzi. Jeśli ktoś znajdzie bardziej ogólne rozwiązanie, wróć i powiedz nam.

JMW
źródło
-2

Innym, używając opcji bash określone zmiennej wewnętrznej $-.

Z .bashrc,

# If not running interactively, don't do anything
case $- in
    *i*) ;;
    *) return;;
esac
xae
źródło
interaktywna powłoka niekoniecznie jest podłączona do terminala. a jeden zaczął z tego połączenia jest uruchamiany automatycznie interaktywny, jest to również możliwe: cmd | sh -i | cmd.
mikeserv
Ten kod jest wykonywany w skrypcie. Nie będzie interaktywny, nawet jeśli działa w terminalu.
Gilles „SO- przestań być zły”