Bagaimana cara menulis skrip yang berfungsi dengan atau tanpa argumen?

13

Saya memiliki skrip bash yang kira-kira seperti ini:

#!/bin/bash

if [ $1 = "--test" ] || [ $1 = "-t" ]; then
    echo "Testing..."
    testing="y"

else
    testing="n"
    echo "Not testing."

fi

Jadi apa yang ingin saya lakukan tidak hanya menjalankannya dengan ./script --testatau ./script -t, tetapi juga tanpa argumen (adil ./script), tetapi tampaknya jika saya melakukannya dengan kode saat ini, outputnya hanya:

./script: line 3: [: =: unary operator expected
./script: line 3: [: =: unary operator expected
Not testing.

Jadi bagaimana saya memprogramnya sehingga menjalankannya tanpa argumen sama sekali hanya akan melakukan elsetanpa membuang kesalahan? Apa yang saya lakukan salah?

Peter Cordes
sumber
1
Anda membutuhkan tanda kurung ganda di sekitar cek Anda. [[]]
Terrance
3
Lihat Bash pitfall # 4
steeldriver
@Terrance Anda tidak membutuhkannya , meskipun Anda harus menggunakannya jika Anda menargetkan bash.
Ruslan

Jawaban:

1

"Cara yang tepat" untuk menggunakan opsi panjang dalam skrip shell adalah melalui utilitas getopt GNU . Ada juga getopt yang merupakan bash built-in , tetapi hanya memungkinkan opsi pendek seperti -t. Beberapa contoh getoptpenggunaan dapat ditemukan di sini .

Berikut ini skrip yang menunjukkan cara saya mendekati pertanyaan Anda. Penjelasan sebagian besar langkah ditambahkan sebagai komentar di dalam skrip itu sendiri.

#!/bin/bash

# GNU getopt allows long options. Letters in -o correspond to
# comma-separated list given in --long.

opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened

set -- $opts # some would prefer using eval set -- "$opts"
# if theres -- as first argument, the script is called without
# option flags
if [ "$1" = "--"  ]; then
    echo "Not testing"
    testing="n"
    # Here we exit, and avoid ever getting to argument parsing loop
    # A more practical case would be to call a function here
    # that performs for no options case
    exit 1
fi

# Although this question asks only for one 
# option flag, the proper use of getopt is with while loop
# That's why it's done so - to show proper form.
while true; do
    case "$1" in
        # spaces are important in the case definition
        -t | --test ) testing="y"; echo "Testing" ;;
    esac
    # Loop will terminate if there's no more  
    # positional parameters to shift
    shift  || break
done

echo "Out of loop"

Dengan beberapa penyederhanaan dan komentar yang dihapus, ini dapat disingkat menjadi:

#!/bin/bash
opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened
set -- $opts    
case "$1" in
    -t | --test ) testing="y"; echo "Testing";;
    --) testing="n"; echo "Not testing";;
esac
Sergiy Kolodyazhnyy
sumber
16

Beberapa cara; dua yang paling jelas adalah:

  • Masukkan $1tanda kutip ganda:if [ "$1" = "--test" ]
  • Periksa jumlah argumen menggunakan $ #

Lebih baik lagi, gunakan getopts.

JayEye
sumber
2
+1 untuk menggunakan getopts. Itu cara terbaik untuk pergi.
Seth
3
Idiom lain yang umum adalah [ "x$1" = "x--test" ]untuk mempertahankan lagi argumen baris perintah yang merupakan operator yang valid untuk [perintah tersebut. (Ini adalah alasan lain mengapa [[ ]]lebih disukai: itu bagian dari sintaks shell, bukan hanya perintah builtin.)
Peter Cordes
7

Anda perlu mengutip variabel Anda di dalam kondisi if. Menggantikan:

if [ $1 = "--test" ] || [ $1 = "-t" ]; then

dengan:

if [ "$1" = '--test' ] || [ "$1" = '-t' ]; then  

Maka akan berhasil:

➜ ~ ./test.sh
Tidak sedang menguji.

Selalu selalu gandakan penawaran variabel Anda!

Seth
sumber
Apakah tanda kutip tunggal itu penting atau dapatkah orang menggunakan tanda kutip ganda?
Jawaban yang tepat tergantung pada shell yang Anda gunakan, tetapi dari pertanyaan Anda, saya berasumsi Anda menggunakan keturunan Bourne-shell. Perbedaan utama antara tanda kutip tunggal dan ganda adalah bahwa ekspansi variabel terjadi di dalam tanda kutip ganda, tetapi tidak di dalam tanda kutip tunggal: $ FOO = bar $ echo "$ FOO" bar $ echo '$ FOO' $ FOO (hmm .... can ' t format kode dalam komentar ...)
JayEye
@ParanoidPanda Anda tidak harus menggunakan tanda kutip tunggal, tidak, tetapi praktik yang baik untuk menggunakannya pada string literal. Dengan begitu Anda tidak akan terkejut nanti jika data string Anda memiliki simbol bash yang ingin diperluas.
Seth
@ParanoidPanda Apa yang dikatakan @JayEye: Dengan tanda kutip tunggal tidak ada foo=bar echo '$foo'cetakan ekspansi variabel $fooke output, tetapi foo=bar echo "$foo"dicetak barsebagai gantinya.
EKons
4

Anda menggunakan bash. Bash memiliki alternatif untuk [: [[. Dengan [[, Anda tidak perlu khawatir apakah akan mengutip, dan Anda mendapatkan lebih banyak operator daripada [, termasuk dukungan yang tepat untuk ||:

if [[ $1 = "--test" || $1 = "-t" ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

Atau Anda dapat menggunakan ekspresi reguler:

if [[ $1 =~ ^(--test|-t) ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

Tentu saja, mengutip yang benar harus selalu dilakukan, tetapi tidak ada alasan untuk tetap [menggunakan bash.

muru
sumber
3

Untuk menguji apakah ada argumen (secara umum, dan melakukan tindakan yang sesuai) Ini harus bekerja:

#!/bin/bash

check=$1

if [ -z "$check" ]; then
  echo "no arguments"
else echo "there was an argument"
fi
Yakub Vlijm
sumber
Mengapa tidak [ -z "$1" ]?
EKons
@ ΈρικΚωνσταντόπουλος Dalam kasus ini, tentu saja, namun dalam skrip, saya memanggil variabel satu kali, merujuknya dalam prosedur, yang biasanya lebih dari sekali. Memutuskan untuk menyimpan protokol dalam sampel.
Jacob Vlijm
Dalam sampel ini Anda tampaknya menggunakan $checksatu kali, sebelum adashift , jadi akan lebih baik untuk menggunakannya $1.
EKons
@ ΈρικΚωνσταντόπουλος Tentu saja tidak, seperti yang disebutkan, ini adalah sampel , untuk digunakan dalam skrip. Dalam skrip, adalah praktik yang buruk untuk berulang kali melakukan hal yang sama berulang kali.
Yakub Vlijm
Saya mengerti apa yang Anda maksudkan, tetapi lebih baik tidak menggunakan shebang #!saat memberikan sampel (Anda harus menggambarkan shell di luar kode).
EKons