Bagaimana skrip Bash dapat mengetahui cara menjalankannya?

10

Saya memiliki skrip Bash yang saya coba buat untuk membantu saya menjalankan perintah yang agak rumit dengan perubahan kecil yang akan ditanyakan kepada saya melalui gema dan membaca.

Saya telah menemukan solusi untuk memaksanya menjalankan terminal untuk menjalankan perintah, tetapi saya tidak tertarik dengan itu. Yang ingin saya lakukan adalah, jika saya keluar dan tekan Enter saja di Nautilus (membuatnya dijalankan dengan Run Software), itu hanya akan dengan lembut mengeluarkan pemberitahuan yang mengatakan "Silakan jalankan ini dari terminal."

Saya bisa membuat popup terjadi - seperti pada saya tahu perintah - tetapi saya tidak bisa mendapatkan skrip Bash untuk mengetahui apakah itu dijalankan di dalam terminal atau tidak, sepertinya selalu berpikir seperti itu. Apakah itu mungkin?

Aescula
sumber

Jawaban:

10

Dari man bashbawah EKSPRESI KONDISI :

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

Dengan asumsi fd 1 standar keluar, if [ -t 1 ]; thenharusnya cocok untuk Anda. The Lanjutan Shell Scripting Guide klaim bahwa -tmenggunakan cara ini akan gagal lebih ssh, dan bahwa tes (menggunakan stdin, stdout tidak) karena itu harus menjadi:

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

-pmenguji apakah ada file dan merupakan pipa bernama. Namun , saya perhatikan secara pengalaman, ini tidak benar bagi saya: -p /dev/stdingagal untuk terminal normal dan sesi ssh sedangkan if [ -t 0 ](atau -t 1) berfungsi dalam kedua kasus (lihat juga komentar Gilles di bawah ini tentang masalah di bagian Advanced Scripting Guide ).


Jika masalah utama adalah konteks khusus dari mana Anda ingin memanggil skrip untuk berperilaku dengan cara yang sesuai dengan konteks itu, Anda dapat menghindari semua teknis ini dan menyelamatkan diri Anda dari keributan dengan menggunakan pembungkus dan variabel khusus:

!#/bin/bash

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

Sebut ini live_script.shatau apa pun dan klik dua kali sebagai gantinya. Anda tentu saja dapat mencapai hal yang sama dengan argumen baris perintah, tetapi bungkus masih akan diperlukan untuk membuat titik dan klik dalam karya peramban file GUI.

goldilocks
sumber
5
ini adalah jawaban yang benar - itu juga bagaimana POSIX mengatakan sebuah shell harus mendeteksi apakah itu interaktif atau tidak.
mikeserv
2
@DanielAmaya - jika Anda mengarahkan input maka skrip tidak dijalankan pada terminal. Pertanyaannya adalah bagaimana mendeteksi apakah skrip dijalankan pada terminal.
mikeserv
2
Apakah Anda yakin tentang penggunaan ||dalam [ … ]seperti itu? Jika Anda menggunakan [[ … ]]maka itu akan baik-baik saja, tetapi biasanya ||digunakan untuk memisahkan perintah, dan [ -t 0merupakan doa yang salah [karena yang terakhir ]tidak ada. Biasanya tidak ada perintah -pjuga. Saya setuju dengan pengujian untuk terminal; itu mungkin cara untuk melakukannya. Itu hanya sintaks yang saya khawatirkan.
Jonathan Leffler
1
@JonathanLeffler Right; yang seharusnya menghasilkan kesalahan sintaks, karena operator shell ||terlihat sebelum ]argumen final yang diperlukan untuk [.
chepner
3
Bagian dari Panduan Bash-Scripting Lanjutan memiliki beberapa kesalahan. PS1bukan tes yang dapat diandalkan untuk mengetahui apakah shell itu interaktif. “Jika skrip perlu menguji apakah skrip berjalan dalam shell interaktif” juga membingungkan: seharusnya jika beberapa kode perlu diuji - skrip biasanya tidak berjalan dalam shell interaktif (tetapi bisa juga, jika bersumber) . Menguji idalam $-adalah cara yang benar untuk menguji apakah shell itu interaktif. Menguji -t 0atau -t 2cara yang benar untuk mengetahui apakah skrip berjalan di terminal, yang berbeda dari yang interaktif.
Gilles 'SANGAT berhenti menjadi jahat'
0

Gunakan variabel bash $ SHLVL untuk mendeteksi tingkat penumpukan shell. Dalam skrip yang dijalankan 'mentah' dengan mengklik ganda itu akan menjadi 1, dalam skrip yang berjalan dalam terminal itu akan 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
sumber
0

Meskipun jawaban goldilocks mungkin benar dalam kasus tipikal, tampaknya memang ada kasus tepi. Dalam kasus saya sendiri, xserver saya dikonfigurasi untuk memulai dari tty1dan tidak pernah meninggalkan tty itu. Jika Xorg stdoutadalah TTY maka tampaknya klien akan memiliki TTY yang ditautkan ke deskriptor file mereka secara default.

Inilah cara saya memecahkan masalah saya:

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

Saya belum menguji ini untuk melihat apakah itu bekerja pada konfigurasi X yang lebih standar, dan saya juga sangat meragukan bahwa ini adalah satu-satunya kasus tepi. Jika ada yang menemukan solusi yang lebih umum berlaku, silakan kembali dan beri tahu kami.

JMW
sumber
-2

Lain, menggunakan opsi bash mengatur variabel internal $-,.

Dari .bashrc,

# If not running interactively, don't do anything
case $- in
    *i*) ;;
    *) return;;
esac
xae
sumber
shell interaktif belum tentu terhubung ke terminal. sementara satu mulai dengan koneksi yang secara otomatis dimulai interaktif, ini juga mungkin: cmd | sh -i | cmd.
mikeserv
Kode ini sedang dieksekusi dalam skrip. Itu tidak akan interaktif, bahkan jika itu berjalan di terminal.
Gilles 'SO- stop being evil'