Periksa jumlah argumen yang diteruskan ke skrip Bash

727

Saya ingin skrip Bash saya untuk mencetak pesan kesalahan jika jumlah argumen yang diperlukan tidak terpenuhi.

Saya mencoba kode berikut:

#!/bin/bash
echo Script name: $0
echo $# arguments 
if [$# -ne 1]; 
    then echo "illegal number of parameters"
fi

Untuk beberapa alasan yang tidak diketahui, saya mengalami kesalahan berikut:

test: line 4: [2: command not found

Apa yang saya lakukan salah?

Naftali
sumber
54
Anda seharusnya tidak memberi nama skrip Anda test. Itulah nama perintah Unix standar, Anda tidak ingin membayangi itu.
Barmar
20
Selalu gunakan spasi di sekitar '[' ('[[') atau '(' ('(') di jika pernyataan dalam bash.
zoska
5
Untuk menambahkan komentar @zoska, Anda perlu spasi sebelum [karena ini diterapkan sebagai perintah, coba 'yang ['.
Daniel Da Cunha
1
contoh yang lebih baik diberikan pada tautan di bawah ini: stackoverflow.com/questions/4341630/...
ramkrishna
3
@Barmar pasti menyebutkannya testbaik-baik saja asalkan tidak ada di PATH?
user253751

Jawaban:

1099

Sama seperti perintah sederhana lainnya, [ ... ]atau testmembutuhkan spasi di antara argumennya.

if [ "$#" -ne 1 ]; then
    echo "Illegal number of parameters"
fi

Atau

if test "$#" -ne 1; then
    echo "Illegal number of parameters"
fi

Saran

Ketika di Bash, lebih suka menggunakan [[ ]]karena tidak melakukan pemisahan kata dan perluasan pathname ke variabel-variabelnya yang mengutip mungkin tidak diperlukan kecuali itu bagian dari ekspresi.

[[ $# -ne 1 ]]

Ini juga memiliki beberapa fitur lain seperti pengelompokan kondisi tanpa tanda kutip, pencocokan pola (pencocokan pola diperluas dengan extglob) dan pencocokan regex.

Contoh berikut memeriksa apakah argumen valid. Ini memungkinkan satu atau dua argumen.

[[ ($# -eq 1 || ($# -eq 2 && $2 == <glob pattern>)) && $1 =~ <regex pattern> ]]

Untuk ekspresi aritmatika murni, menggunakan (( ))beberapa mungkin masih lebih baik, tetapi mereka masih mungkin dalam [[ ]]dengan operator aritmatika yang suka -eq, -ne, -lt, -le, -gt, atau -gedengan menempatkan ekspresi sebagai argumen string tunggal:

A=1
[[ 'A + 1' -eq 2 ]] && echo true  ## Prints true.

Itu seharusnya membantu jika Anda perlu menggabungkannya dengan fitur-fitur lain [[ ]]juga.

Keluar dari skrip

Ini juga logis untuk membuat skrip keluar ketika parameter yang tidak valid diteruskan ke sana. Ini telah disarankan dalam komentar oleh ekangas tetapi seseorang mengedit jawaban ini untuk menjadikannya -1sebagai nilai yang dikembalikan, jadi saya mungkin melakukannya dengan benar.

-1meskipun diterima oleh Bash sebagai argumen untuk exittidak didokumentasikan secara eksplisit dan tidak benar untuk digunakan sebagai saran umum. 64juga merupakan nilai paling formal karena sudah didefinisikan sysexits.hdengan #define EX_USAGE 64 /* command line usage error */. Sebagian besar alat suka lsjuga mengembalikan 2argumen yang tidak valid. Saya juga biasa mengembalikan 2skrip saya tetapi belakangan ini saya tidak lagi peduli, dan hanya digunakan 1dalam semua kesalahan. Tapi mari kita letakkan di 2sini karena ini paling umum dan mungkin tidak spesifik untuk OS.

if [[ $# -ne 1 ]]; then
    echo "Illegal number of parameters"
    exit 2
fi

Referensi

konsolebox
sumber
2
OP: Ingatlah bahwa [itu hanyalah perintah lain, yaitu, cobalah which [.
Leo
5
Perintah @Leo dapat dibangun, dan bisa juga tidak. Dalam bash, [adalah builtin, sedangkan [[kata kunci. Dalam beberapa kerang yang lebih tua, [bahkan tidak builtin. Perintah seperti [secara alami ada bersama sebagai perintah eksternal di sebagian besar sistem, tetapi perintah internal diprioritaskan oleh shell kecuali jika Anda memotong dengan commandatau exec. Periksa dokumentasi shell tentang bagaimana mereka mengevaluasi. Perhatikan perbedaan mereka, dan bagaimana mereka dapat berperilaku berbeda di setiap shell.
konsolebox
78

Ini mungkin ide yang baik untuk menggunakan ekspresi aritmatika jika Anda berurusan dengan angka.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi
Aleks-Daniel Jakimenko-A.
sumber
Mengapa itu ide yang bagus, dalam kasus sekarang? Mempertimbangkan efisiensi, portabilitas dan masalah-masalah lain, bukankah lebih baik menggunakan sintaksis yang paling sederhana dan paling dipahami secara universal, yaitu [ ... ], ketika hal ini berhasil dengan baik dan tidak diperlukan operasi mewah?
Maks
@Max ekspansi aritmatika $(( ))tidak mewah dan harus diimplementasikan oleh semua shell POSIX. Namun, (( ))sintaks (tanpa $) bukan bagian dari itu. Jika Anda karena alasan tertentu terbatas, maka tentunya Anda dapat menggunakannya [ ]sebagai gantinya, tetapi perlu diingat bahwa Anda tidak boleh menggunakannya [[ ]]juga. Saya harap Anda memahami perangkap [ ]dan alasan mengapa fitur ini ada. Tapi ini adalah pertanyaan Bash jadi kami memberikan jawaban Bash ( "Sebagai aturan praktis, [[digunakan untuk string dan file. Jika Anda ingin membandingkan angka, gunakan ArithmeticExpression" ).
Aleks-Daniel Jakimenko-A.
40

Pada []:! =, =, == ... adalah operator perbandingan string dan -eq, -gt ... adalah aritmatika biner .

Saya akan menggunakan:

if [ "$#" != "1" ]; then

Atau:

if [ $# -eq 1 ]; then
jhvaras
sumber
9
==sebenarnya adalah fitur tidak berdokumen, yang kebetulan berfungsi dengan GNU test. Ini juga terjadi untuk bekerja dengan FreeBSD test, tetapi mungkin tidak berfungsi pada foo test . Satu- satunya perbandingan standar adalah =(hanya FYI).
Martin Tournoij
1
Ini didokumentasikan pada entri bash man: Ketika operator == dan! = Digunakan, string di sebelah kanan operator dianggap sebagai pola dan dicocokkan sesuai dengan aturan yang dijelaskan di bawah di bawah Pencocokan Pola. Jika opsi shell nocasematch diaktifkan, pertandingan dilakukan tanpa memperhatikan kasus karakter alfabet. Nilai kembali adalah 0 jika string cocok (==) atau tidak cocok (! =) Polanya, dan 1 sebaliknya. Bagian mana pun dari pola dapat dikutip untuk memaksa agar dicocokkan sebagai string.
jhvaras
2
@ jhvaras: Persis seperti yang dikatakan Carpetsmoker: ini dapat bekerja di beberapa implementasi (dan memang, ia bekerja di Bash), tetapi tidak sesuai dengan POSIX . Sebagai contoh, akan gagal dengan dash: dash -c '[ 1 == 1 ]'. POSIX hanya menentukan =, dan tidak ==.
gniourf_gniourf
34

Jika Anda hanya tertarik untuk membayar jika argumen tertentu tidak ada, Substitusi Parameter bagus:

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Script exits here if command-line parameter absent,
#+ with following error message.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT
Menepuk
sumber
bukankah itu sarat dengan bashism?
Dwight Spencer
@DwightSpencer Apakah itu penting?
konsolebox
@Temak Saya bisa jika Anda memiliki pertanyaan khusus, tetapi artikel yang ditautkan menjelaskannya lebih baik daripada saya.
Pat
13

Satu liner sederhana yang berfungsi dapat dilakukan menggunakan:

[ "$#" -ne 1 ] && ( usage && exit 1 ) || main

Ini dipecah menjadi:

  1. uji variabel bash untuk ukuran parameter $ # tidak sama dengan 1 (jumlah sub perintah kami)
  2. jika benar maka panggil fungsi use () dan keluar dengan status 1
  3. selain itu panggil fungsi utama ()

Berpikir untuk diperhatikan:

  • penggunaan () dapat berupa gema sederhana "$ 0: params"
  • main bisa berupa satu skrip panjang
Dwight Spencer
sumber
1
Jika Anda memiliki satu set garis lain setelah garis itu, itu akan salah karena exit 1hanya akan berlaku untuk konteks subkulit membuatnya hanya identik dengan ( usage; false ). Saya bukan penggemar penyederhanaan seperti itu ketika datang ke opsi-parsing, tetapi Anda dapat menggunakannya { usage && exit 1; }sebagai gantinya. Atau mungkin saja { usage; exit 1; }.
konsolebox
1
@konsolebox (penggunaan && keluar 1) berfungsi untuk ksh, zsh, dan bash, kembali ke bash 2.0. Sintaks {...} hanya baru untuk 4.0+ bash. Jangan salah paham jika salah satu cara berfungsi dengan baik untuk Anda, maka gunakan itu tetapi ingat tidak semua orang menggunakan implementasi bash yang sama dengan yang Anda lakukan dan kami harus membuat kode untuk posix standar bukan bashism.
Dwight Spencer
Saya tidak yakin apa yang Anda katakan. {...}adalah sintaksis umum dan tersedia untuk sebagian besar jika tidak semua shell berdasarkan sh, bahkan shell yang lebih tua tidak mengikuti standar POSIX.
konsolebox
7

Lihat ini pesta cheatsheet, dapat membantu banyak.

Untuk memeriksa panjang argumen yang diteruskan, Anda gunakan "$#"

Untuk menggunakan array argumen yang diteruskan, Anda menggunakan "$@"

Contoh memeriksa panjang, dan iterasi adalah:

myFunc() {
  if [[ "$#" -gt 0 ]]; then
    for arg in "$@"; do
      echo $arg
    done
  fi
}

myFunc "$@"

Articled ini membantu saya, tetapi kehilangan beberapa hal untuk saya dan situasi saya. Semoga ini bisa membantu seseorang.

Nick Hall
sumber
0

Jika Anda ingin berada di sisi yang aman, saya sarankan untuk menggunakan getopts.

Ini adalah contoh kecil:

    while getopts "x:c" opt; do
      case $opt in
        c)
          echo "-$opt was triggered, deploy to ci account" >&2
          DEPLOY_CI_ACCT="true"
          ;;
            x)
              echo "-$opt was triggered, Parameter: $OPTARG" >&2 
              CMD_TO_EXEC=${OPTARG}
              ;;
            \?)
              echo "Invalid option: -$OPTARG" >&2 
              Usage
              exit 1
              ;;
            :)
              echo "Option -$OPTARG requires an argument." >&2 
              Usage
              exit 1
              ;;
          esac
        done

lihat lebih detail di sini misalnya http://wiki.bash-hackers.org/howto/getopts_tutorial

Ishak
sumber
0

Di sini sederhana satu liner untuk memeriksa apakah hanya satu parameter yang diberikan jika tidak keluar dari skrip:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit
panticz
sumber
-1

Anda harus menambahkan spasi di antara kondisi pengujian:

if [ $# -ne 1 ]; 
    then echo "illegal number of parameters"
fi

Saya harap ini membantu.

Fabricio
sumber