Linux shell script: Jalankan program hanya jika ada, abaikan saja jika tidak ada

15

Saya sedang memprogram skrip shell Linux yang akan mencetak status banner selama pelaksanaannya hanya jika alat yang tepat, katakanlah figlet, diinstal (ini: dapat dijangkau di jalur sistem ).

Contoh:

#!/usr/bin/env bash
echo "foo"
figlet "Starting"
echo "moo"
figlet "Working"
echo "foo moo"
figlet "Finished"

Saya ingin untuk naskah saya bekerja tanpa kesalahan bahkan ketika figletsedang tidak diinstal .

Apa yang bisa menjadi metode praktis ?

Sopalajo de Arrierez
sumber
@sudodus: mengabaikan perintah 'figlet' (dan parameternya) akan beres. Eksekusi yang berkelanjutan, tentu saja.
Sopalajo de Arrierez
2
Judul untuk pertanyaan ini membuat saya dalam segala macam masalah metafisik
g_uint
2
Apakah Anda ingin mengabaikan semua kesalahan? Gunakan saja figlet ... || true.
Giacomo Alzetta
Jika Anda tidak peduli dengan kode keluar, jalan pintas harus digunakan figlet || true, tetapi dalam kasus Anda mungkin fungsi shell yang tidak dapat dicetak.
eckes

Jawaban:

30

Interpretasi saya akan menggunakan fungsi pembungkus bernama sama dengan alat; dalam fungsi itu, jalankan alat nyata jika ada:

figlet() {
  command -v figlet >/dev/null && command figlet "$@"
}

Maka Anda bisa figlet arg1 arg2...tidak mengubah skrip Anda.

@Olorin datang dengan metode yang lebih sederhana: tentukan fungsi wrapper hanya jika kita perlu (jika alat tidak ada):

if ! command -v figlet > /dev/null; then figlet() { :; }; fi

Jika Anda ingin argumen yang figletakan dicetak bahkan jika figlet tidak diinstal, sesuaikan saran Olorin sebagai berikut:

if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi
Jeff Schaller
sumber
1
Dari mana datangnya command? Saya tidak memilikinya di instalasi saya (Red Hat 6.8 Enterprise dan Cygwin64).
eewanco
1
@ eewanco, lihat unix.stackexchange.com/a/85250/117549 ; Singkatnya, itu dibangun untuk bash, yang mungkin shell Anda.
Jeff Schaller
4
commandadalah built-in POSIX Bourne shell. Ini biasanya mengeksekusi perintah yang diberikan, tetapi -vflag membuatnya berperilaku lebih seperti type, shell lain builtin.
wyrm
@ eewanco type -a commandakan menunjukkan kepada Anda. hanyawhich akan menampilkan file yang dapat dieksekusi pada Anda , bukan bawaan, kata kunci, fungsi, atau alias. $PATH
l0b0
@ l0b0: di RedHat-family dengan bash dan profil default (s) which tidak menemukan alias yang berlaku, karena alias whichsendiri untuk menjalankan /usr/bin/whichdan pipa itu daftar alias shell untuk melihat (!) (Tentu saja \whichmenekan alias dan hanya menggunakan program, yang tidak menunjukkan alias.)
dave_thompson_085
14

Anda dapat menguji apakah figletada

if type figlet >/dev/null 2>&1
then
    echo Figlet is installed
fi
roaima
sumber
6

Cara umum untuk melakukan ini adalah dengan test -xalias [ -x. Berikut ini contoh yang diambil dari /etc/init.d/ntpsistem Linux:

if [ -x /usr/bin/lockfile-create ]; then
    lockfile-create $LOCKFILE
    lockfile-touch $LOCKFILE &
    LOCKTOUCHPID="$!"
fi

Varian ini bergantung pada mengetahui path lengkap dari executable. Dalam /bin/lesspipesaya menemukan contoh yang bekerja di sekitar itu dengan menggabungkan -xdan whichperintah:

if [ -x "`which bunzip`" ]; then bunzip -c "$1"
else echo "No bunzip available"; fi ;;

Cara ini akan bekerja tanpa mengetahui terlebih dahulu mana di PATHdalam bunzipexecutable adalah.

kasperd
sumber
4
Jangan gunakanwhich . Dan bahkan jika whichberhasil, menggunakan test -xpada outputnya konyol: jika Anda mendapatkan jalan dari which, itu ada.
Gilles 'SANGAT berhenti menjadi jahat'
1
@Gilles: Secara teknis, test -xtidak lebih dari sekadar memeriksa apakah ada file (untuk itulah test -e). Itu juga memeriksa apakah file telah menetapkan izin eksekusi.
comfreak
@ comfreak Begitu juga which.
Gilles 'SANGAT berhenti menjadi jahat'
5

Di awal skrip Anda, periksa apakah figlet ada, dan jika tidak, tentukan fungsi shell yang tidak melakukan apa-apa:

type figlet >/dev/null 2>&1 || figlet() { :; }

typememeriksa apakah figletada sebagai built-in shell, fungsi, alias, atau kata kunci,>/dev/null 2>&1 membuang stdin dan stdout sehingga Anda tidak mendapatkan output, dan jika tidak ada, figlet() { :; }mendefinisikan figletsebagai fungsi yang tidak melakukan apa-apa.

Dengan cara ini Anda tidak perlu mengedit setiap baris skrip yang digunakan figlet , atau memeriksa apakah ada setiap kali figletdipanggil.

Anda dapat menambahkan pesan diagnostik, jika Anda suka:

type figlet >/dev/null 2>&1 || { echo 'figlet not installed.' ; figlet() { :; } ; }

Sebagai bonus, karena Anda tidak menyebutkan shell mana yang Anda gunakan, saya percaya ini adalah POSIX compliant, jadi itu harus bekerja pada sebagian besar shell.

Chris
sumber
Saya suka kesederhanaan jawaban ini. Anda juga bisa mengganti type figlet >/dev/null 2>&1dengan hash figlet 2>/dev/nulljika Anda menggunakan bash. (OP mengatakan "jika itu ada di PATH saya".)
Joe
5

Alternatif lain - pola yang saya lihat di skrip konfigurasi proyek otomatis:

if [ -x /usr/bin/figlet ]
then
    FIGLET=/usr/bin/figlet
else
    FIGLET=:
fi

$FIGLET "Hello, world!"

Dalam kasus spesifik Anda, Anda bahkan dapat melakukannya,

if [ -x /usr/bin/figlet ]
then
   SAY=/usr/bin/figlet
elif [ -x /usr/local/bin/figlet ]
then
   SAY=/usr/local/bin/figlet
elif [ -x /usr/bin/banner ]
then
   SAY=/usr/bin/banner
else
   SAY=/usr/bin/echo
fi

$SAY "Hello, world!"

Jika Anda tidak tahu jalur spesifik, Anda dapat mencoba beberapa elif(lihat di atas) untuk mencoba lokasi yang diketahui, atau cukup gunakan PATHuntuk selalu menyelesaikan perintah:

if command -v figlet >/dev/null
then
    SAY=figlet
elif command -v banner >/dev/null
then
    SAY=banner
else
    SAY=echo
fi

Secara umum, saat menulis skrip, saya lebih suka hanya memanggil perintah di lokasi tertentu yang ditentukan oleh saya. Saya tidak suka ketidakpastian / risiko apa yang mungkin dimasukkan pengguna akhir ke dalam merekaPATH , mungkin di mereka sendiri ~/bin.

Jika, misalnya, saya menulis skrip yang rumit untuk orang lain yang mungkin menghapus file berdasarkan output dari perintah tertentu yang saya panggil, saya tidak ingin secara tidak sengaja mengambil sesuatu di dalamnya ~/binyang mungkin atau mungkin bukan perintah Saya mengharapkan.

rrauenza
sumber
3
Bagaimana jika figletada di dalam /usr/local/binatau /home/bob/stuff/programs/executable/figlet?
Gilles 'SANGAT berhenti menjadi jahat'
3
type -p figlet > /dev/null && figlet "foo"

typePerintah bash menemukan perintah, fungsi, alias, kata kunci, atau builtin (lihat help type) dan mencetak lokasi atau definisi. Itu juga mengembalikan kode kembali yang mewakili hasil pencarian; true (0) jika ditemukan. Jadi yang kami lakukan di sini adalah mencoba menemukan figletdi jalur ( -pberarti hanya mencari file, bukan built-in atau fungsi, dan juga menekan pesan kesalahan), membuang output (itulah yang > /dev/nulldilakukan), dan jika itu mengembalikan true ( &&) , itu akan dieksekusifiglet .

Ini lebih sederhana jika figletberada di lokasi tetap:

[ -x /usr/bin/figlet ] && /usr/bin/figlet "foo"

Di sini kita menggunakan testperintah (alias [) untuk melihat apakah /usr/bin/figletdieksekusi ( -x) dan jika demikian (&& ) jalankan. Solusi ini saya pikir lebih portabel daripada menggunakan typeyang merupakan bashism yang saya percaya

Anda bisa membuat fungsi yang melakukan ini untuk Anda:

function x() {
    if type -p "$1" >/dev/null; then
        cmd="$1"
        shift
        "$cmd" "$@"
    fi
}

(Kutipan diperlukan karena ruang potensial)

Maka Anda hanya akan melakukan:

x figlet "foo"
eewanco
sumber
0

o /, aku akan mengatakan sesuatu seperti

#!/usr/bin/env bash
# if figlet is installed :
if [ "$(which figlet 2>/dev/null)" ]; then
       # do what you wanted to do
       echo "foo"
       figlet "Starting"
       echo "moo"
       figlet "Working"
       echo "foo moo"
       figlet "Finished"
# if not
else
       # exit program with an error
       echo "please install figlet"
       exit 1
fi
Jus de Patate_
sumber
0

Anda dapat melakukan eksekusi pengujian, menekan output apa pun, dan menguji kode keberhasilan / kegagalan. Pilih argumen untuk figlet untuk membuat tes ini tidak mahal. -? atau --help atau --version adalah kemungkinan yang jelas.

if figlet --help >/dev/null 2>&1 ; then
    # figlet is available
    echo "foo"
    figlet "starting"
    #etc
else
    rc=$?
    echo "figlet is not installed or not working correctly (return code ${rc})"
fi

Ditambahkan dalam menanggapi komentar di bawah ini: jika Anda benar-benar ingin menguji figlet yang ada, bukan bahwa itu dapat digunakan, maka Anda akan melakukan

figlet --help >/dev/null 2>&1 
rc=$?
if [ $rc -eq 127 ] ; then # 127 is "command not found" on linux bash 4.4.23(1)
    echo "command figlet not found"
else
    figlet "whatever" # use figlet
fi
nigel222
sumber
Masalahnya di sini adalah bahwa figlet bisa gagal karena alasan selain tidak diinstal, jadi hanya menguji kode keluar tidak cukup.
Chris
Jika Anda benar-benar ingin menguji hanya untuk instalasinya, maka Anda ingin menguji apakah $?sama dengan 127 (pada sistem linux saya). 127 adalah "perintah tidak ditemukan". Tetapi pemikiran saya adalah bahwa untuk perintah rasional, jika command --helpgagal, maka instalasinya cukup jelas sehingga mungkin tidak ada di sana !.
nigel222
1
126 adalah "perintah ditemukan tetapi tidak dapat dieksekusi," jadi Anda juga ingin mengujinya. Sayangnya, Anda tidak dapat bergantung pada --helpketersediaan. Utilitas Posix tidak memilikinya, salah satunya, dan pedoman posix sebenarnya merekomendasikannya . at --helpgagal dengan at: invalid option -- '-', misalnya, dan status keluar 130di sistem saya.
Chris
Juga, tentu saja, jika figlet dapat keluar dengan status 127 yang bisa menjadi masalah juga.
Chris
Poin wajar sekitar 126. Sebagian besar utilitas waras memiliki beberapa opsi perintah ringan yang tidak berbahaya, seperti -? --help --version... atau Anda bisa mengetahui apa yang figlet -0 --illegalkeluar, dan memperlakukannya sebagai indikator keberhasilan Anda (asalkan bukan 127, yang akan saya klasifikasikan sebagai sabotase).
nigel222