Kesalahan skrip Bash [:! =: Diharapkan operator unary

96

Dalam skrip saya, saya mencoba memeriksa kesalahan apakah argumen pertama dan satu-satunya sama dengan -v tetapi ini adalah argumen opsional. Saya menggunakan pernyataan if tetapi saya terus mendapatkan kesalahan yang diharapkan operator unary.

ini kodenya:

if [ $1 != -v ]; then
   echo "usage: $0 [-v]"
   exit
fi

Edit:

Saya harus lebih spesifik: Bagian dari skrip di atas memeriksa argumen opsional dan kemudian, jika argumen tidak dimasukkan, ia harus menjalankan program lainnya.

#!/bin/bash

if [ "$#" -gt "1" ]; then
   echo "usage: $0 [-v]"
   exit
fi

if [ "$1" != -v ]; then
   echo "usage: $0 [-v]"
   exit
fi

if [ "$1" = -v ]; then
   echo "`ps -ef | grep -v '\['`"
else
   echo "`ps -ef | grep '\[' | grep root`"
fi
pengguna3380240
sumber
... ngomong-ngomong, saya pikir Anda ingin echo "usage: $0 [-v]"; $-menunjukkan tanda opsi shell aktif, bukan nama skrip saat ini.
Charles Duffy
Saya memiliki bagian itu dengan benar, saya ingin itu menunjukkan nama skrip saat ini.
pengguna3380240
4
Selamat datang di stackoverflow, dan khususnya di tag bash! Lihat tag wiki untuk alat dan sumber daya yang berguna, seperti shellcheck yang akan menunjukkan (meskipun tidak selalu menjelaskan) banyak masalah seperti ini.
orang lain itu
@ user3380240, $-adalah tidak nama script saat ini. $0adalah.
Charles Duffy
Maaf, itu salah ketik.
pengguna3380240

Jawaban:

189

Tanda kutip!

if [ "$1" != -v ]; then

Jika tidak, ketika $1benar-benar kosong, pengujian Anda menjadi:

[ != -v ]

dari pada

[ "" != -v ]

... dan !=bukan operator unary (yaitu, yang hanya mampu mengambil satu argumen).

Charles Duffy
sumber
8
Atau, jika Anda tidak peduli dengan portabilitas, Anda dapat menggunakan tanda kurung ganda, di dalamnya ekspansi variabel tidak perlu dikutip: if [[ $1 != -v ]]; then
Mike Holt
@MikeHolt, memang - Saya mengemukakannya dalam komentar atas pertanyaan di atas.
Charles Duffy
@DanielDinnyes, jika IFS=1, maka [ $# -eq 1 ]tidak akan berperilaku baik, sedangkan [ "$#" -eq 1 ]berperilaku seperti yang diinginkan sekalipun. Ini kasus patologis, tentu, tetapi lebih baik menulis perangkat lunak yang tidak memilikinya ketika diberi pilihan.
Charles Duffy
-2

Atau untuk apa yang tampak seperti pembantaian yang merajalela, tetapi sebenarnya sederhana ... Cukup banyak mencakup semua kasus Anda, dan tidak ada string kosong atau masalah yang tidak disadari.

Dalam kasus ini, argumen pertama adalah '-v', lalu lakukan kondisional Anda ps -ef, jika tidak, dalam semua kasus lainnya buang penggunaan.

#!/bin/sh
case $1 in
  '-v') if [ "$1" = -v ]; then
         echo "`ps -ef | grep -v '\['`"
        else
         echo "`ps -ef | grep '\[' | grep root`"
        fi;;
     *) echo "usage: $0 [-v]"
        exit 1;; #It is good practice to throw a code, hence allowing $? check
esac

Jika seseorang tidak peduli di mana argumen '-v' berada, maka cukup letakkan case di dalam satu lingkaran. Itu akan memungkinkan berjalannya semua argumen dan menemukan '-v' di mana saja (asalkan ada). Ini berarti urutan argumen baris perintah tidak penting. Diperingatkan, seperti yang disajikan, variabel arg_match disetel, jadi ini hanyalah sebuah bendera. Ini memungkinkan beberapa kemunculan argumen '-v'. Seseorang dapat mengabaikan semua kemunculan '-v' lainnya dengan cukup mudah.

#!/bin/sh

usage ()
 {
  echo "usage: $0 [-v]"
  exit 1
 }

unset arg_match

for arg in $*
 do
  case $arg in
    '-v') if [ "$arg" = -v ]; then
           echo "`ps -ef | grep -v '\['`"
          else
           echo "`ps -ef | grep '\[' | grep root`"
          fi
          arg_match=1;; # this is set, but could increment.
       *) ;;
  esac
done

if [ ! $arg_match ]
 then
  usage
fi

Tapi, memungkinkan beberapa kemunculan argumen nyaman digunakan dalam situasi seperti:

$ adduser -u:sam -s -f -u:bob -trace -verbose

Kami tidak peduli tentang urutan argumen, dan bahkan mengizinkan beberapa argumen -u. Ya, mengizinkan juga:

$ adduser -u sam -s -f -u bob -trace -verbose
SMullaney
sumber
$*tidak boleh digunakan dalam konteks ini: Ini menggabungkan item menjadi string yang merupakan string-split dan glob-expand; tidak seperti "$@", yang meninggalkan item dengan nilai aslinya yang tepat. Dan Anda kehilangan beberapa kutipan, yang akan ditangkap shellcheck.net (dengan peringatan yang ditautkan ke halaman wiki yang menjelaskan mengapa kutipan itu penting).
Charles Duffy
Pertimbangkan, sebagai contoh konkret, -U'Bob Barker'; for arg in $*akan melihatnya sebagai -UBobdan kemudian Barkersebagai item terpisah; sedangkan for item in "$@"akan terlihat -UBob Barkersebagai string tunggal.
Charles Duffy