ตรวจสอบการมีอยู่ของอาร์กิวเมนต์อินพุตในสคริปต์เชลล์ Bash


1336

ฉันต้องการตรวจสอบการมีอยู่ของอาร์กิวเมนต์อินพุต ฉันมีสคริปต์ต่อไปนี้

if [ "$1" -gt "-1" ]
  then echo hi
fi

ฉันเข้าใจ

[: : integer expression expected

ฉันจะตรวจสอบอินพุตอาร์กิวเมนต์ 1 ก่อนเพื่อดูว่ามีอยู่ได้อย่างไร?

คำตอบ:


2328

มันคือ:

if [ $# -eq 0 ]
  then
    echo "No arguments supplied"
fi

$#ตัวแปรจะบอกจำนวนของการขัดแย้งการป้อนข้อมูลสคริปต์ถูกผ่าน

หรือคุณสามารถตรวจสอบว่าอาร์กิวเมนต์เป็นสตริงว่างหรือไม่:

if [ -z "$1" ]
  then
    echo "No argument supplied"
fi

-zสวิทช์จะทดสอบว่าการขยายตัวของ "$ 1" เป็นสตริงโมฆะหรือไม่ ถ้ามันเป็นสตริงที่เป็นโมฆะร่างกายจะถูกดำเนินการ


62
ฉันชอบที่จะทำเช่นนี้ในรูปแบบไวยากรณ์สั้นและยังคงยอมรับ POSIX [ -z "$1" ] && echo "No argument supplied" ฉันชอบหนึ่งสมุทรเนื่องจากพวกเขาง่ายสำหรับฉัน และยังเร็วกว่าเพื่อตรวจสอบมูลค่าทางออกเมื่อเทียบกับการใช้if
JM Becker

168
คุณอาจต้องการที่จะเพิ่มexit 1ในตอนท้ายของ echos ของคุณในบล็อกถ้าเมื่ออาร์กิวเมนต์ที่จำเป็นสำหรับสคริปต์ที่จะทำงาน เห็นได้ชัดว่าคุ้มค่าที่จะต้องพิจารณา
msanford

16
เป็นไปได้แม้ว่าจะไม่ค่อยมีประโยชน์สำหรับการโต้แย้งแรกจะเริ่มต้น แต่ว่างเปล่า; programname "" secondarg third. การ$#ตรวจสอบจะตรวจสอบจำนวนอาร์กิวเมนต์อย่างไม่น่าสงสัย
tripleee

39
สำหรับ noob โดยเฉพาะอย่างยิ่งคนที่มาจากภูมิหลังที่ไม่ใช่สคริปต์มันเป็นสิ่งสำคัญที่จะกล่าวถึงลักษณะเฉพาะบางอย่างเกี่ยวกับสิ่งเหล่านี้ คุณอาจกล่าวได้ว่าเราต้องการพื้นที่ว่างหลังจากการเปิดและวงเล็บปิด มิฉะนั้นสิ่งต่าง ๆ จะไม่ทำงาน ฉันเป็น noob สคริปต์ (ฉันมาจากพื้นหลัง C) และพบว่ามันยาก มันเป็นเพียงเมื่อฉันตัดสินใจที่จะคัดลอกทุกสิ่ง "ตามที่เป็น" สิ่งที่ทำงานให้ฉัน ตอนนั้นฉันก็รู้ว่าฉันต้องออกจากที่ว่างหลังจากที่เปิดเหล็กและก่อนที่จะปิด
HighOnMeat

72
และสำหรับ args เพิ่มเติมif [ ! -z "$1" ]; then ...
gcb

346

มันเป็นการดีกว่าที่จะสาธิตวิธีนี้

if [[ $# -eq 0 ]] ; then
    echo 'some message'
    exit 1
fi

ตามปกติคุณจะต้องออกหากคุณมีข้อโต้แย้งน้อยเกินไป


72
ไม่มันไม่ใช่: สิ่งนี้มีexit 1สิ่งที่คุณต้องการและใช้การ[[ ]]ทดสอบที่ (iirc) มักจะสมเหตุสมผลกว่า ดังนั้นสำหรับคนคัดลอกวางรหัสสุ่มสี่สุ่มห้านี่คือคำตอบที่ดีกว่า
dshepherd

42
หากต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับความแตกต่างระหว่าง [] และ [[]] ดูstackoverflow.com/questions/3427872/…
Sebastián Grignoli

103

ในบางกรณีคุณต้องตรวจสอบว่าผู้ใช้ผ่านการโต้แย้งไปยังสคริปต์และหากไม่ให้ถอยกลับไปเป็นค่าเริ่มต้น ชอบในสคริปต์ด้านล่าง:

scale=${2:-1}
emulator @$1 -scale $scale

ที่นี่หากผู้ใช้ไม่ได้ผ่านscaleเป็นพารามิเตอร์ตัวที่สองฉันจะเรียกใช้ตัวจำลอง Android ด้วย-scale 1ค่าเริ่มต้น ${varname:-word}เป็นผู้ประกอบการขยายตัว มีผู้ประกอบการขยายตัวอื่น ๆ เช่นกัน:

  • ${varname:=word}ซึ่งตั้งค่าไม่ได้กำหนดvarnameแทนการคืนwordค่า;
  • ${varname:?message}ซึ่งผลตอบแทนvarnameถ้ามันถูกกำหนดและไม่เป็นโมฆะหรือพิมพ์messageและยกเลิกสคริปต์ (เช่นตัวอย่างแรก);
  • ${varname:+word}ซึ่งจะส่งกลับwordเฉพาะเมื่อvarnameมีการกำหนดและไม่เป็นโมฆะ ส่งกลับค่า null มิฉะนั้น

1
${varname?message}ตัวอย่างข้างต้นดูเหมือนว่าจะใช้ เป็นตัว:พิมพ์พิเศษหรือเปลี่ยนพฤติกรรมหรือไม่
Eki

6
Eki, ":" เป็นคำสั่ง builtin และจดชวเลขสำหรับ / bin / true ในตัวอย่างนี้ เพราะมันหมายถึงคำสั่ง do-nothing ที่ละเว้นข้อโต้แย้งที่มีให้ จำเป็นอย่างยิ่งในการทดสอบนี้เพื่อป้องกันไม่ให้ล่ามพยายามดำเนินการกับเนื้อหาของ "$ varname" (ซึ่งแน่นอนว่าคุณไม่ต้องการให้เกิดขึ้น) นอกจากนี้ยังมีมูลค่า noting; คุณสามารถทดสอบตัวแปรได้หลายวิธีด้วยวิธีนี้ตามที่คุณต้องการ และทั้งหมดมีข้อผิดพลาดเฉพาะ ie: ${1?"First argument is null"} ${2?"Please provide more than 1 argument"}
user.friendly

48

ลอง:

 #!/bin/bash
 if [ "$#" -eq  "0" ]
   then
     echo "No arguments supplied"
 else
     echo "Hello world"
 fi

4
ทำไมคุณต้องมีเครื่องหมายคำพูดคู่$#และ0?
user13107

1
ไม่มีปัญหาถ้าเราใช้โดยไม่ต้องใส่เครื่องหมายคำพูดคู่เหมือน $ # และ 0
Ranjithkumar T

บน windows mingw นี่เป็นวิธีเดียวที่จะไป
Lajos Meszaros

2
คำตอบนี้ให้จุดเริ่มต้นที่ดีสำหรับสคริปต์ที่ฉันเพิ่งทำ ขอบคุณสำหรับการแสดงelseเช่นกัน
Chris K

2
@ user13107 สองตัวแปรที่ยกมาในทุบตีป้องกัน globbing (เช่นการขยายชื่อไฟล์เช่นfoo*) และการแยกคำ (เช่นการแยกเนื้อหาถ้าค่ามีช่องว่าง) ในกรณีนี้ไม่จำเป็นต้องอ้าง$#เพราะทั้งสองกรณีไม่ได้ใช้ การอ้างถึง0ยังไม่จำเป็น แต่บางคนชอบที่จะอ้างถึงค่าเพราะมันเป็นสตริงจริง ๆ และทำให้ชัดเจนยิ่งขึ้น
Dennis

39

อีกวิธีในการตรวจสอบว่ามีการส่งอาร์กิวเมนต์ไปที่สคริปต์หรือไม่:

((!$#)) && echo No arguments supplied!

ทราบว่า(( expr ))เป็นสาเหตุของการแสดงออกที่จะได้รับการประเมินตามกฎของเชลล์เลขคณิต

เพื่อที่จะออกในกรณีที่ไม่มีข้อโต้แย้งใด ๆ เราสามารถพูดได้:

((!$#)) && echo No arguments supplied! && exit 1

อีกวิธีหนึ่งที่คล้ายคลึงกับการพูดข้างต้นคือ:

let $# || echo No arguments supplied

let $# || { echo No arguments supplied; exit 1; }  # Exit if no arguments!

help let พูดว่า:

let: let arg [arg ...]

  Evaluate arithmetic expressions.

  ...

  Exit Status:
  If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.

2
-1 นี่อาจเป็นวิธีที่เลวร้ายที่สุดหากตรวจสอบการมีอยู่ของอาร์กิวเมนต์ .. และมันสามารถทริกเกอร์การทดแทนประวัติและอาจทำสิ่งที่ไม่ดี
มิตร

2
แทนที่จะexitฆ่ากระบวนการ zsh ของฉันฉันใช้returnซึ่งไม่ฆ่ามัน
Timo

เหตุใดจึง((!$#))เรียกการทดแทนประวัติ
Zhro

25

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

#!/bin/bash

if [ -z "$1" ]; then
    echo -e "\nPlease call '$0 <argument>' to run this command!\n"
    exit 1
fi

1
ดังนั้นสิ่งนี้จะถูกใช้ในคุณต้องการเพียงหนึ่งอาร์กิวเมนต์?
Danijel

21

เพียงเพราะมีจุดฐานมากขึ้นที่จะชี้ให้เห็นฉันจะเพิ่มที่คุณสามารถทดสอบสตริงของคุณเป็นโมฆะ:

if [ "$1" ]; then
  echo yes
else
  echo no
fi

เช่นเดียวกันหากคุณคาดหวังว่า arg จะนับเพียงทดสอบครั้งสุดท้ายของคุณ:

if [ "$3" ]; then
  echo has args correct or not
else
  echo fixme
fi

และอื่น ๆ ด้วย arg หรือ var


4

หากคุณต้องการตรวจสอบว่ามีอาร์กิวเมนต์อยู่หรือไม่คุณสามารถตรวจสอบว่า # ของอาร์กิวเมนต์นั้นมากกว่าหรือเท่ากับหมายเลขอาร์กิวเมนต์เป้าหมายของคุณ

สคริปต์ต่อไปนี้แสดงให้เห็นถึงวิธีการทำงาน

test.sh

#!/usr/bin/env bash

if [ $# -ge 3 ]
then
  echo script has at least 3 arguments
fi

สร้างเอาต์พุตต่อไปนี้

$ ./test.sh
~
$ ./test.sh 1
~
$ ./test.sh 1 2
~
$ ./test.sh 1 2 3
script has at least 3 arguments
$ ./test.sh 1 2 3 4
script has at least 3 arguments

3

เป็นตัวเตือนเล็ก ๆ ที่ผู้ประกอบการทดสอบที่เป็นตัวเลขในทุบตีเท่านั้นทำงานในจำนวนเต็ม ( -eq, -lt, -geฯลฯ )

ฉันต้องการให้แน่ใจว่า $ vars ของฉันเป็น ints

var=$(( var + 0 ))

ก่อนที่ฉันจะทดสอบพวกเขาเพียงเพื่อป้องกันข้อผิดพลาด "[: จำเป็นต้องระบุจำนวนเต็ม ARG"


1
เคล็ดลับเรียบร้อย แต่โปรดทราบ: เนื่องจากไม่สามารถใช้ bash ในการจัดการลอยในแบบเลขคณิตวิธีการนี้อาจทำให้เกิดข้อผิดพลาดทางไวยากรณ์และส่งกลับค่าที่ไม่ใช่ศูนย์ซึ่งจะเป็นอุปสรรคในการเปิดใช้งาน errexit var=$(printf "%.0f" "$var")สามารถจัดการลอย แต่ทนทุกข์ทรมานจากการออกที่ไม่เป็นศูนย์เมื่อได้รับสตริง หากคุณไม่ได้ใจ awk var=$(<<<"$var" awk '{printf "%.0f", $0}')นี้ใช้วิธีการที่ผมน่าจะเป็นที่แข็งแกร่งที่สุดสำหรับการบังคับใช้จำนวนเต็ม: หากไม่ได้ตั้งค่า var จะมีค่าเริ่มต้นเป็น "0" ถ้า var เป็นทศนิยมมันจะถูกปัดเศษเป็นจำนวนเต็มที่ใกล้เคียงที่สุด ค่าลบก็ใช้ได้เช่นกัน
มิตร

0

การตรวจสอบฟังก์ชั่นซับหนึ่งทุบตี

myFunction() {

    : ${1?"forgot to supply an argument"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

เพิ่มชื่อฟังก์ชั่นและการใช้งาน

myFunction() {

    : ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

เพิ่มการตรวจสอบเพื่อตรวจสอบว่าจำนวนเต็ม

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

: ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"} && validateIntegers $1 || die "Must supply an integer!"

จากนั้นสร้างฟังก์ชันการตรวจสอบความถูกต้องที่ตรวจสอบความถูกต้องของอาร์กิวเมนต์คืนค่า 0 เมื่อสำเร็จ, 1 เมื่อล้มเหลวและฟังก์ชันตายที่ยกเลิกสคริปต์เมื่อเกิดความล้มเหลว

validateIntegers() {

    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
        return 1 # failure
    fi
    return 0 #success

}

die() { echo "$*" 1>&2 ; exit 1; }

ง่ายกว่า - เพียงใช้ set -u

set -u ทำให้แน่ใจว่าทุกตัวแปรที่อ้างอิงถูกตั้งค่าไว้เมื่อมีการใช้งานดังนั้นเพียงแค่ตั้งค่าและลืมมันไป

myFunction() {
    set -u
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

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