Bagaimana cara menangani sakelar dalam skrip shell?

17

Apakah ada beberapa alat bawaan yang akan mengenali -xdan --xxxxsebagai saklar, dan bukan argumen, atau apakah Anda harus melalui semua variabel input, menguji tanda hubung, dan kemudian menguraikan argumen setelahnya?

pengguna394
sumber

Jawaban:

16

Gunakan getopts.

Ini cukup portabel seperti pada spec POSIX. Sayangnya itu tidak mendukung opsi lama.

Lihat juga getopts tutorial ini dari wiki bash-hacker dan pertanyaan ini dari stackoverflow.

Jika Anda hanya memerlukan opsi pendek, pola penggunaan umum untuk getopts(menggunakan pelaporan kesalahan tidak diam) adalah:

# process arguments "$1", "$2", ... (i.e. "$@")
while getopts "ab:" opt; do
    case $opt in
    a) aflag=true ;; # Handle -a
    b) barg=$OPTARG ;; # Handle -b argument
    \?) ;; # Handle error: unknown option or missing required argument.
    esac
done
jw013
sumber
3
Harus disebutkan bahwa getoptharus selalu diverifikasi sebagai GNU getopt sebelum Anda menggunakannya, tetapi Anda tidak boleh menggunakannya karena getoptslebih portabel (dan umumnya lebih baik). Jika Anda lakukan perlu menyebutnya untuk beberapa alasan, menyebutnya dengan cara GNU khusus, dan memastikan bahwa GETOPT_COMPATIBLEtidak dalam lingkungan.
Chris Down
Apa yang dilakukan usus besar while getopts "ab:" opt?
user394
1
@ user394 Huruf :setelah yang menandakan itu membutuhkan argumen. A :sebagai karakter pertama berarti untuk menekan pesan kesalahan.
jw013
22

Saya menganggap Anda menggunakan bash atau serupa. Sebuah contoh:

all=false
long=false

while getopts ":hal" option; do
  case $option in
    h) echo "usage: $0 [-h] [-a] [-l] file ..."; exit ;;
    a) all=true ;;
    l) long=true ;;
    ?) echo "error: option -$OPTARG is not implemented"; exit ;;
  esac
done

# remove the options from the positional parameters
shift $(( OPTIND - 1 ))

ls_opts=()
$all && ls_opts+=( -a )
$long && ls_opts+=( -l )

# now, do it
ls "${ls_opts[@]}" "$@"
glenn jackman
sumber
1
+1 untuk digunakan +=dengan array. Tidak tahu kamu bisa melakukan itu. Bagus!
James Sneeringer
5

Anda harus menulis siklus untuk mem-parsing parameter. Memang Anda bisa menggunakan getoptsperintah untuk melakukannya dengan mudah.

Ini adalah contoh sederhana dari getoptshalaman manual:

aflag=
bflag=
while getopts ab: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval="$OPTARG";;
    ?)    printf "Usage: %s: [-a] [-b value] args\n" $0
          exit 2;;
    esac
done
if [ ! -z "$aflag" ]; then
    printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
    printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"
andcoz
sumber
2

Saya menulis skrip baru-baru ini untuk pekerjaan yang fleksibel dan memungkinkan untuk beberapa jenis switch dalam urutan apa pun. Saya tidak dapat mengungkapkan skrip lengkap untuk alasan hukum yang jelas (belum lagi saya tidak memilikinya bersama saya saat ini), tetapi inilah dagingnya .. Anda dapat memasukkannya ke dalam subrutin dan menyebutnya di akhir naskah Anda:

options () {

    if [ -n "$1" ]; then # test if any arguments passed - $1 will always exist
        while (( "$#" )); do  # process ALL arguments
            if [ "$1" = ^-t$ ]; then # -t short for "test"
                # do something here THEN shift the argument
                # shift removes it from $@ and reduces $# by
                # one if you supply no argument
                shift

            # we can also process multiple arguments at once
            elif [[ "$1" =~ ^--test=[:alnum:]{1,8}$ ]] && [[ "$2" =~ ^-t2$ ]] && [[ -n "$3" ]]; then # check for 3 arguments
                # do something more then remove them ALL from the arg list    
                shift 3
            else
                echo "No matching arguments!"
                echo "Usage: [script options list here]"
            fi
        done
    else
        echo "Usage: [script options list here]"
        exit 0
    fi
}

options "$@" # run options and loop through/process ALL arguments

Saya sarankan membatasi skrip bash Anda menjadi kurang dari 400 baris / 15k karakter; skrip saya yang tadi tumbuh melebihi ukuran ini dan menjadi sangat sulit untuk dikerjakan. Saya mulai menulis ulang di Perl, yang jauh lebih cocok untuk tugas itu. Ingatlah hal itu saat Anda mengerjakan skrip Anda di bash. Bash bagus untuk skrip dan oneliner kecil, tetapi ada yang lebih kompleks dan Anda lebih baik menulisnya di Perl.

Catatan, saya belum menguji di atas, jadi mungkin tidak berhasil, tetapi Anda mendapatkan ide umum dari itu.

laebshade
sumber
Cara Anda menelepon optionspada akhirnya tidak benar, itu akan kembali -bash: syntax error near unexpected token $@. Sebut saja sebagai options "$@".
Chris Down
Yup, saya mencampuradukkan Perl dan Bash. Dikoreksi.
laebshade
Itu whilekondisi seharusnya tidak (($#))bukan?
manatwork
Untuk apa Anda membutuhkan dua set tanda kurung $#? Sunting: Anda benar. Memperbaikinya kewhile (( "$#" ))
laebshade