Bash script error [:! =: unary operator ที่คาดไว้


99

ในสคริปต์ของฉันฉันพยายามตรวจสอบข้อผิดพลาดว่าอาร์กิวเมนต์แรกและอาร์กิวเมนต์เดียวเท่ากับ -v หรือไม่ แต่เป็นอาร์กิวเมนต์ที่เป็นทางเลือก ฉันใช้คำสั่ง if แต่ได้รับข้อผิดพลาดที่คาดว่าจะได้รับตัวดำเนินการยูนารี

นี่คือรหัส:

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

แก้ไข:

ฉันควรจะเจาะจงมากขึ้น: ส่วนนี้ของสคริปต์ข้างต้นกำลังตรวจสอบอาร์กิวเมนต์ที่เป็นทางเลือกจากนั้นหากไม่ได้ป้อนอาร์กิวเมนต์ก็ควรเรียกใช้ส่วนที่เหลือของโปรแกรม

#!/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

... ยังไงก็ตามฉันคิดว่าคุณต้องการecho "usage: $0 [-v]"; $-แสดงแฟล็กอ็อพชันเชลล์ที่แอ็คทีฟไม่ใช่ชื่อของสคริปต์ปัจจุบัน
Charles Duffy

ฉันมีส่วนนั้นถูกต้องฉันต้องการให้มันแสดงชื่อของสคริปต์ปัจจุบัน
user3380240

4
ยินดีต้อนรับสู่ stackoverflow และโดยเฉพาะแท็ก bash! ตรวจสอบแท็กวิกิสำหรับเครื่องมือและทรัพยากรที่มีประโยชน์เช่นshellcheckซึ่งจะชี้ให้เห็น (แม้ว่าจะไม่ได้อธิบายเสมอไป) ปัญหามากมายเช่นนี้
ผู้ชายคนนั้น

@ user3380240, $-คือไม่ได้ชื่อของสคริปต์ปัจจุบัน $0คือ.
Charles Duffy

ขออภัยนั่นเป็นการพิมพ์ผิด
user3380240

คำตอบ:


196

คำคม!

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

มิฉะนั้นเมื่อ$1ใดที่ว่างเปล่าการทดสอบของคุณจะกลายเป็น:

[ != -v ]

แทน

[ "" != -v ]

... และ!=ไม่ใช่ตัวดำเนินการแบบยูนารี (นั่นคือสามารถรับอาร์กิวเมนต์เดียวได้เท่านั้น)


9
หรือหากคุณไม่กังวลเกี่ยวกับความสามารถในการพกพาคุณสามารถใช้วงเล็บสองชั้นซึ่งไม่จำเป็นต้องมีการขยายตัวแปร: if [[ $1 != -v ]]; then
Mike Holt

@MikeHolt จริง - ฉันนำสิ่งนั้นมาแสดงความคิดเห็นในคำถามด้านบน
Charles Duffy

@DanielDinnyes ถ้าIFS=1อย่างนั้น[ $# -eq 1 ]จะไม่ประพฤติดีในขณะที่[ "$#" -eq 1 ]ประพฤติตามที่ตั้งใจไว้ เป็นกรณีทางพยาธิวิทยาแน่นอน แต่ควรเขียนซอฟต์แวร์ที่ไม่มีพวกเขาเมื่อได้รับเลือก
Charles Duffy

-2

หรือสำหรับสิ่งที่ดูเหมือนอาละวาดมากเกินไป แต่เป็นเรื่องง่าย ... ค่อนข้างครอบคลุมทุกกรณีของคุณและไม่มีสตริงว่างเปล่าหรือความกังวลที่ไม่ชัดเจน

ในกรณีที่อาร์กิวเมนต์แรกคือ '-v' ให้ทำตามเงื่อนไขของคุณps -efมิฉะนั้นในกรณีอื่น ๆ ทั้งหมดจะทำให้การใช้งานเกิดขึ้น

#!/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

หากไม่มีใครสนใจว่าอาร์กิวเมนต์ '-v' อยู่ตรงไหนให้วางเคสไว้ในลูป อนุญาตให้เดิน args ทั้งหมดและค้นหา '-v' ได้ทุกที่ (หากมีอยู่) ซึ่งหมายความว่าลำดับอาร์กิวเมนต์บรรทัดคำสั่งไม่สำคัญ โปรดทราบล่วงหน้าตามที่นำเสนอตัวแปร arg_match ถูกตั้งค่าดังนั้นจึงเป็นเพียงแฟล็ก อนุญาตให้เกิดอาร์กิวเมนต์ '-v' ได้หลายครั้ง เราสามารถเพิกเฉยต่อเหตุการณ์อื่น ๆ ทั้งหมดของ '-v' ได้ง่ายพอ

#!/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

แต่อนุญาตให้เกิดอาร์กิวเมนต์หลายครั้งได้สะดวกในการใช้งานในสถานการณ์เช่น:

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

เราไม่สนใจลำดับของอาร์กิวเมนต์และแม้แต่อนุญาตให้มีอาร์กิวเมนต์หลาย -u ใช่มันเป็นเรื่องง่ายที่จะอนุญาต:

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

$*ไม่ควรใช้ในบริบทนี้: มันเชื่อมต่อไอเท็มเป็นสตริงซึ่งมีทั้งแบบแยกสตริงและแบบขยาย ไม่เหมือน"$@"ซึ่งทำให้รายการมีค่าดั้งเดิมที่แม่นยำ และคุณพลาดคำพูดบางคำซึ่งshellcheck.netจะจับได้ (โดยคำเตือนที่เชื่อมโยงกับหน้าวิกิที่อธิบายว่าเหตุใดคำพูดเหล่านั้นจึงมีความสำคัญ)
Charles Duffy

พิจารณาเป็นตัวอย่างที่เป็นรูปธรรม-U'Bob Barker'; for arg in $*จะเห็นว่ามันเป็น-UBobแล้วBarkerเป็นรายการแยกต่างหาก; ในขณะที่for item in "$@"จะเห็น-UBob Barkerเป็นสตริงเดียว
Charles Duffy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.