ตรวจสอบจำนวนอาร์กิวเมนต์ที่ส่งผ่านไปยังสคริปต์ Bash


727

ฉันต้องการให้สคริปต์ Bash ของฉันพิมพ์ข้อความแสดงข้อผิดพลาดหากไม่นับจำนวนอาร์กิวเมนต์ที่ต้องการ

ฉันลองรหัสต่อไปนี้:

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

ด้วยเหตุผลที่ไม่ทราบสาเหตุบางอย่างฉันได้รับข้อผิดพลาดต่อไปนี้:

test: line 4: [2: command not found

ผมทำอะไรผิดหรือเปล่า?


54
testคุณไม่ควรตั้งชื่อสคริปต์ของคุณ นั่นคือชื่อของคำสั่ง Unix มาตรฐานคุณไม่ต้องการเงามัน
Barmar

20
ใช้ช่องว่างรอบ ๆ '[' ('[[') หรือ '(' ('(())) หากคำสั่งเป็น bash
zoska

5
ในการเพิ่มความคิดเห็น @zoska คุณต้องเว้นวรรคก่อน [เนื่องจากมีการใช้งานเป็นคำสั่งให้ลอง 'ซึ่ง ['
Daniel Da Cunha

1
ตัวอย่างที่ดีกว่ามีให้ในลิงค์ด้านล่าง: stackoverflow.com/questions/4341630/…
ramkrishna

3
@Barmar การตั้งชื่อแน่นอนว่ามันใช้ได้testดีตราบใดที่มันไม่ได้อยู่ใน PATH?
user253751

คำตอบ:


1099

เช่นเดียวกับคำสั่งง่ายๆอื่น ๆ[ ... ]หรือtestต้องการเว้นวรรคระหว่างอาร์กิวเมนต์

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

หรือ

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

ข้อเสนอแนะ

เมื่ออยู่ใน Bash คุณควรใช้[[ ]]แทนเพราะจะไม่ทำการแยกคำและขยายชื่อพา ธ ไปยังตัวแปรที่อาจไม่จำเป็นต้องมีการอ้างอิงเว้นแต่เป็นส่วนหนึ่งของการแสดงออก

[[ $# -ne 1 ]]

นอกจากนี้ยังมีคุณสมบัติอื่น ๆ เช่นการจัดกลุ่มเงื่อนไขที่ไม่ได้ระบุการจับคู่รูปแบบ (การจับคู่รูปแบบขยายด้วยextglob) และการจับคู่ regex

ตัวอย่างต่อไปนี้ตรวจสอบว่าข้อโต้แย้งนั้นถูกต้องหรือไม่ อนุญาตให้มีอาร์กิวเมนต์เดียวหรือสองรายการ

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

สำหรับการแสดงออกทางคณิตศาสตร์บริสุทธิ์โดยใช้(( ))บางส่วนอาจจะยังคงดีขึ้น แต่พวกเขายังคงเป็นไปได้ใน[[ ]]กับผู้ประกอบการทางคณิตศาสตร์ที่มันต้องการ-eq, -ne, -lt, -le, -gtหรือ-geโดยการวางการแสดงออกเป็นอาร์กิวเมนต์สายเดียว:

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

มันจะมีประโยชน์ถ้าคุณจะต้องรวมมันกับคุณสมบัติอื่น ๆ ของ[[ ]]เช่นกัน

ออกจากสคริปต์

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

-1แม้ว่าจะได้รับการยอมรับจาก Bash ว่าเป็นข้อโต้แย้งที่exitไม่ชัดเจนและไม่ถูกต้องที่จะใช้เป็นข้อเสนอแนะทั่วไป 64นอกจากนี้ยังมีค่าที่สุดอย่างเป็นทางการตั้งแต่มันกำหนดไว้ในด้วยsysexits.h #define EX_USAGE 64 /* command line usage error */เครื่องมือส่วนใหญ่เช่นlsกลับ2ด้วยอาร์กิวเมนต์ที่ไม่ถูกต้อง ฉันยังเคยกลับมา2ในสคริปต์ของฉัน แต่เมื่อเร็ว ๆ นี้ฉันไม่ได้ใส่ใจจริงๆและใช้1ในข้อผิดพลาดทั้งหมด แต่เรามาที่2นี่เพราะมันเป็นเรื่องธรรมดาที่สุดและอาจไม่ใช่ระบบปฏิบัติการเฉพาะ

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

อ้างอิง


2
OP: เก็บไว้ในใจว่าเป็นเพียงคำสั่งอื่นเช่นลอง[ which [
Leo

5
@Leo คำสั่งสามารถสร้างได้และไม่สามารถทำได้ ในทุบตี[เป็นตัวในขณะที่[[เป็นคำหลัก ในหอยรุ่นเก่าบางตัว[ไม่ได้สร้างขึ้นมา คำสั่งที่ชอบ[ธรรมชาติอยู่ร่วมในฐานะที่เป็นคำสั่งภายนอกในระบบมากที่สุด แต่คำสั่งภายในจะจัดลำดับความสำคัญจากเปลือกเว้นแต่คุณอ้อมด้วยหรือcommand execตรวจสอบเอกสารของเชลล์ว่าพวกเขาประเมินอย่างไร จดบันทึกความแตกต่างและวิธีที่พวกเขาอาจมีพฤติกรรมแตกต่างกันในทุก ๆ เปลือก
konsolebox

77

มันอาจเป็นความคิดที่ดีที่จะใช้นิพจน์ทางคณิตศาสตร์หากคุณกำลังติดต่อกับตัวเลข

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi

เหตุใดจึงเป็นความคิดที่ดีในกรณีปัจจุบัน เมื่อพิจารณาถึงประสิทธิภาพความสะดวกในการพกพาและปัญหาอื่น ๆ ไม่ควรใช้รูปแบบที่ง่ายและเข้าใจได้ดีที่สุดในระดับสากลเช่น[ ... ]เมื่องานนี้ใช้ได้ดีและไม่จำเป็นต้องมีการดำเนินการที่แปลกใหม่?
สูงสุด

@Max การขยายเลขคณิต$(( ))ไม่ได้เป็นรูปแบบแฟนซีและควรนำมาใช้โดยเชลล์ POSIX ทั้งหมด อย่างไรก็ตาม(( ))ไวยากรณ์ (ไม่มี$) ไม่ได้เป็นส่วนหนึ่งของมัน หากคุณมีข้อ จำกัด ด้วยเหตุผลบางอย่างคุณสามารถใช้[ ]แทนได้ แต่โปรดจำไว้ว่าคุณไม่ควรใช้[[ ]]ด้วย ฉันหวังว่าคุณจะเข้าใจถึงข้อผิดพลาด[ ]และสาเหตุที่คุณสมบัติเหล่านี้มีอยู่ แต่นี่เป็นคำถามของ Bash ดังนั้นเราจึงให้คำตอบของ Bash ( “ ตามกฎง่ายๆ, [[ใช้สำหรับสตริงและไฟล์หากคุณต้องการเปรียบเทียบตัวเลขให้ใช้ ArithmeticExpression” )
Aleks-Daniel Jakimenko-A

39

เมื่อ []:! =, =, == ... คือ :!ตัวดำเนินการเปรียบเทียบสตริงและ -eq, -gt ... เป็นเลขฐานสองแบบเลขคณิต

ฉันจะใช้:

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

หรือ:

if [ $# -eq 1 ]; then

9
==เป็นจริงคุณลักษณะที่ไม่มีเอกสารซึ่งเกิดขึ้นในการทำงานกับ testGNU นอกจากนี้ยังเกิดขึ้นในการทำงานกับ FreeBSD testแต่อาจไม่ทำงานบนfoo testการเปรียบเทียบแบบมาตรฐานเท่านั้นคือ=(เพียง FYI)
Martin Tournoij

1
มีการบันทึกไว้ในรายการ bash man: เมื่อใช้ตัวดำเนินการ == และ! = สตริงทางด้านขวาของตัวดำเนินการจะถือเป็นรูปแบบและจับคู่ตามกฎที่อธิบายด้านล่างภายใต้การจับคู่รูปแบบ หากเปิดใช้งานตัวเลือกเชลล์ nocasematch การจับคู่จะดำเนินการโดยไม่คำนึงถึงตัวอักษรพิมพ์ใหญ่ ค่าที่ส่งคืนคือ 0 ถ้าสตริงตรงกับ (==) หรือไม่ตรงกับ (! =) รูปแบบและ 1 เป็นอย่างอื่น ส่วนใดส่วนหนึ่งของรูปแบบอาจถูกยกมาเพื่อบังคับให้มันถูกจับคู่เป็นสตริง
jhvaras

2
@jhvaras: นั่นคือสิ่งที่ Carpetsmoker กล่าวว่ามันอาจจะทำงานในการใช้งานบางคน (และแน่นอนว่าการทำงานในทุบตี) แต่มันไม่ได้เป็น POSIX ยกตัวอย่างเช่นมันจะล้มเหลวกับ:dash dash -c '[ 1 == 1 ]'POSIX เพียงระบุและไม่= ==
gniourf_gniourf

34

หากคุณสนใจเพียงการประกันตัวหากมีข้อโต้แย้งใด ๆ หายไปการทดแทนพารามิเตอร์นั้นยอดเยี่ยม:

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

ไม่เต็มไปด้วย bashisms?
Dwight Spencer

@DwightSpencer มันจะสำคัญหรือไม่
konsolebox

@Temak ฉันสามารถทำได้หากคุณมีคำถามเฉพาะ แต่บทความที่เชื่อมโยงไปยังอธิบายได้ดีกว่าที่ฉันสามารถทำได้
Pat

13

ซับง่าย ๆ ที่ใช้งานได้สามารถทำได้โดยใช้:

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

สิ่งนี้จะแบ่งเป็น:

  1. ทดสอบตัวแปร bash สำหรับขนาดของพารามิเตอร์ $ # ไม่เท่ากับ 1 (จำนวนคำสั่งย่อยของเรา)
  2. ถ้าเป็นจริงแล้วฟังก์ชั่นการใช้งานการโทร () และออกด้วยสถานะ 1
  3. อื่นเรียกฟังก์ชั่น main ()

คิดว่าควรทราบ:

  • การใช้งาน () สามารถเป็นเพียงเสียงสะท้อนง่ายๆ "$ 0: params"
  • หลักสามารถเป็นหนึ่งสคริปต์ยาว

1
หากคุณมีชุดของเส้นหลังบรรทัดที่ว่าคนอื่นว่าจะมีความผิดตั้งแต่exit 1เท่านั้นที่จะนำไปใช้กับบริบทของ subshell ( usage; false )ที่ทำให้มันเป็นเพียงแค่ตรงกัน ฉันไม่ใช่แฟนตัวยงของการทำให้เข้าใจง่ายแบบนั้นเมื่อพูดถึงการแยกวิเคราะห์ตัวเลือก แต่คุณสามารถใช้{ usage && exit 1; }แทนได้ { usage; exit 1; }หรืออาจเป็นเพียงแค่
konsolebox

1
@konsolebox (การใช้งาน && ออก 1) ใช้งานได้กับ ksh, zsh และ bash เมื่อกลับไปที่ bash 2.0 ไวยากรณ์ {... } เป็นเวอร์ชันล่าสุดของ bash 4.0+ เท่านั้น อย่าเข้าใจฉันผิดถ้าวิธีหนึ่งใช้งานได้ดีสำหรับคุณแล้วใช้งานได้ แต่อย่าจำไว้ว่าทุกคนใช้การดำเนินการทุบตีแบบเดียวกับที่คุณทำ
Dwight Spencer

ฉันไม่แน่ใจว่าคุณกำลังพูดอะไร {...}เป็นไวยากรณ์ทั่วไปและสามารถใช้ได้กับเชลล์ส่วนใหญ่หากไม่ใช่เชลล์ทั้งหมดที่ใช้เชลล์shที่เก่ากว่านั้นจะไม่เป็นไปตามมาตรฐาน POSIX
konsolebox

7

ตรวจสอบนี้สูตรชีท bashมันสามารถช่วยได้มาก

ในการตรวจสอบความยาวของข้อโต้แย้งที่ส่งผ่านคุณใช้ "$#"

ในการใช้อาเรย์ของอาร์กิวเมนต์ที่ผ่านเข้ามาคุณใช้ "$@"

ตัวอย่างของการตรวจสอบความยาวและการวนซ้ำจะเป็น:

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

myFunc "$@"

บทความนี้ช่วยฉัน แต่ก็หายไปเล็กน้อยสำหรับฉันและสถานการณ์ของฉัน หวังว่านี่จะช่วยใครซักคน


0

ในกรณีที่คุณต้องการที่จะปลอดภัยฉันขอแนะนำให้ใช้ getopts

นี่คือตัวอย่างเล็ก ๆ :

    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

ดูรายละเอียดเพิ่มเติมได้ที่นี่เช่นhttp://wiki.bash-hackers.org/howto/getopts_tutorial


0

นี่คือ liners ง่ายๆเพื่อตรวจสอบว่ามีเพียงพารามิเตอร์เดียวเท่านั้นที่กำหนดให้มิฉะนั้นออกจากสคริปต์:

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

-1

คุณควรเพิ่มช่องว่างระหว่างเงื่อนไขการทดสอบ:

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

ฉันหวังว่านี่จะช่วยได้.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.