ทำไมไม่ใช้หลายคำสั่งด้วย || หรือ && ทำงานตามเงื่อนไขหรือไม่


12

ใช้งานได้ในเชลล์ (ทุบตี, เส้นประ)

[ -z "" ] && echo A || echo B
A

อย่างไรก็ตามฉันพยายามเขียนเชลล์สคริปต์POSIXมันเริ่มต้นดังนี้:

#!/bin/sh

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

readonly raw_input_string=${1}

[ -z "${raw_input_string}" ] && echo "The given argument is empty."; exit 1

และฉันไม่รู้ว่าทำไม แต่ฉันไม่ได้รับข้อความ :

อาร์กิวเมนต์ที่ให้นั้นว่างเปล่า

ถ้าฉันเรียกสคริปต์เช่นนี้:

./test_empty_argument ""

ทำไมถึงเป็นอย่างนั้น?


5
ดูฉันจะทดสอบได้อย่างไรว่าตัวแปรว่างเปล่าหรือมีช่องว่างเท่านั้น? สำหรับวิธีในการทดสอบว่าตัวแปรว่างเปล่าตั้งค่าหรือมีเพียงช่องว่าง ปัญหาในคำถามนี้ไม่เกี่ยวข้องกับเรื่องนั้น
ilkkachu

1
เพียงใช้if [ X”” = X”$var” ] ; then echo isempty ; fi
user2497

3
@ user2497 ไม่มีเหตุผลที่จะใช้ในเชลล์ใด ๆ ที่เปิดตัวใน 20 ปีที่ผ่านมา นั่นเป็นวิธีแก้ปัญหาสำหรับกระสุนรถถังเก่า
chepner

@chepner ดังนั้นจึงไม่ใช่วิธีที่ถูกต้องใช่ไหม ต้องใช้อย่างอื่นอีกไหม?
user2497

6
[ "" = "$var" ]จะทำงานได้ดี; [สตริงที่ว่างเปล่าที่ยกมาจะไม่ถูกลบออกจากรายการอาร์กิวเมนต์ของ แต่นั่นไม่ใช่สิ่งที่จำเป็นอย่างใดอย่างหนึ่งเพราะ[ -z "$var" ] ยังทำงานได้ดี
chepner

คำตอบ:


37

โปรดสังเกตว่าสายของคุณ

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

นี่เป็นเช่นเดียวกับ

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."
exit 1

( ;ในกรณีส่วนใหญ่สามารถถูกแทนที่ด้วยอักขระขึ้นบรรทัดใหม่)

ซึ่งหมายความว่าexit 1คำสั่งจะถูกดำเนินการเสมอโดยไม่คำนึงถึงจำนวนอาร์กิวเมนต์ที่ถูกส่งผ่านไปยังสคริปต์ นี่หมายความว่าข้อความThe given argument is empty.นั้นจะไม่มีโอกาสได้รับการพิมพ์ออกมา

ในการดำเนินการมากกว่าคำเดียวหลังการทดสอบโดยใช้ไวยากรณ์ "ลัดวงจร" { ...; }กลุ่มงบใน ทางเลือกคือการใช้ifคำสั่งที่เหมาะสม(ซึ่ง IMHO ดูสะอาดตาในสคริปต์):

if [ "$#" -ne 1 ]; then
    echo 'Invalid number of arguments, expected one.' >&2
    exit 1
fi

คุณมีปัญหาเดียวกันกับการทดสอบครั้งที่สองของคุณ


เกี่ยวกับ

[ -z "" ] && echo A || echo B

นี้จะใช้งานได้สำหรับตัวอย่างที่กำหนด แต่ทั่วไป

some-test && command1 || command2

จะไม่เหมือนกับ

if some-test; then
    command1
else
    command2
fi

แทนที่จะเป็นเช่นนั้นอีก

if ! { some-test && command1; }; then
    command2
fi

หรือ

if some-test && command1; then
    :
else
    command2
fi

นั่นคือถ้าการทดสอบหรือคำสั่งแรกล้มเหลวคำสั่งที่สองจะดำเนินการซึ่งหมายความว่ามันมีศักยภาพในการดำเนินการคำสั่งที่เกี่ยวข้องทั้งสาม


18

นี้:

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

ไม่ใช่:

[ "${#}" -eq 1 ] || { echo "Invalid number of arguments, expected one."; exit 1; }

แต่แทนที่จะเป็น:

{ [ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; } 
exit 1

สคริปต์ของคุณกำลังออกโดยไม่คำนึงถึงจำนวนอาร์กิวเมนต์ที่คุณส่งไปให้


8

วิธีหนึ่งที่จะทำให้อ่านง่ายขึ้นคือการกำหนดdieฟังก์ชั่น (à perl) เช่น:

die() {
  printf >&2 '%s\n' "$@"
  exit 1
}

# then:

[ "$#" -eq 1 ] || die "Expected one argument, got $#"

[ -n "$1" ] || die "Empty argument not supported"

คุณสามารถเพิ่ม bells และ whistles เช่นสีคำนำหน้าหมายเลขบรรทัด ... ถ้าจำเป็น


ในทางปฏิบัติคุณเคยเรียกdieฟังก์ชั่นของคุณโดยมีหลายอาร์กิวเมนต์หรือไม่? (ถ้าเป็นเช่นนั้นคุณสามารถยกตัวอย่างได้หรือไม่?) ฉันใช้dieฟังก์ชั่นที่เกือบเหมือนกันแต่ใช้"$*"แทนซึ่งอาจเป็นสิ่งที่คุณตั้งใจมากกว่า
jrw32982 รองรับ Monica

3
ค่าของ"$@"คือการอนุญาตให้ข้อความหลายบรรทัดโดยไม่จำเป็นต้องเพิ่มบรรทัดใหม่ตามตัวอักษร
Charles Duffy

1
@ jrw32982 การใช้"$*"เพื่อเข้าร่วม args ด้วยช่องว่างหมายความว่าคุณต้องตั้งค่า$IFSเป็น SPC เพื่อให้สามารถทำงานในบริบททั้งหมดรวมถึงที่ที่$IFSมีการแก้ไข อีกทางเลือกหนึ่งที่มีksh/ zshคุณสามารถใช้print -r -- "$@"หรือในecho -E - "$@" zsh
Stéphane Chazelas

@CharlesDuffy ใช่ แต่ฉันไม่เคยเห็นสิ่งที่ทำในบริบทของdieฟังก์ชัน -type สิ่งที่ฉันถามคือในทางปฏิบัติคุณเคยเห็นใครเขียนdie "unable to blah:" "some error"เพื่อจุดประสงค์ในการรับข้อความแสดงข้อผิดพลาด 2 บรรทัดหรือไม่
jrw32982 รองรับ Monica

@ StéphaneChazelasจุดที่ดี ดังนั้น (ในการกำหนดของฉัน) die() { IFS=" "; printf >&2 "%s\n" "$*"; exit 1; }มันควรจะเป็น คุณเคยใช้dieฟังก์ชั่นประเภทนี้เป็นการส่วนตัวเพื่อprintfสร้างข้อความแสดงข้อผิดพลาดหลายบรรทัดโดยผ่านการขัดแย้งหลายครั้งหรือไม่? หรือคุณเคยผ่านการโต้แย้งเพียงครั้งเดียวเพื่อdieให้มันเพิ่มบรรทัดใหม่เดียวในการส่งออกหรือไม่
jrw32982 รองรับ Monica

-1

ฉันมักจะเห็นสิ่งนี้เป็นการทดสอบสตริงว่าง:

if [ "x$foo" = "x" ]; then ...

ควรได้รับ "=" - คงที่
WEF

3
การฝึกฝนนั้นมาจากยุค 1970 อย่างแท้จริง ไม่มีเหตุผลใด ๆ ที่จะใช้กับเปลือกใด ๆ ที่สอดคล้องกับ 1992 มาตรฐานการดวลจุดโทษ POSIX (ตราบใดที่ถูกต้องอ้างถูกนำมาใช้และการทำงานในขณะนี้ล้าสมัยเช่นไม่เป็น-a, -o, (และ)เป็น derectives จะบอกtestที่จะรวมการดำเนินงานหลายในการภาวนาเดียว ถูกหลีกเลี่ยงดูที่เครื่องหมาย OB ในpubs.opengroup.org/onlinepubs/9699919799/utilities/test.html )
Charles Duffy

ลินุกซ์ที่ไม่ใช่ของลินุกซ์ส่งมาพร้อมกับ 'sh' ดั้งเดิมที่ดีใน '90's - อาจยังทำอยู่ ในงานของฉันในเวลานั้นฉันต้องเขียนสคริปต์การติดตั้งแบบพกพาและฉันใช้โครงสร้างนี้ ฉันเพิ่งดูสคริปต์การติดตั้งที่ NVidia จัดส่งให้กับ linux และพวกเขายังคงใช้โครงสร้างนี้
เรา

NVidia อาจใช้มัน แต่นั่นไม่ได้บอกว่าพวกเขามีเหตุผลทางเทคนิคที่จะทำเช่นนั้น การพัฒนาสินค้า - ลัทธิในระบบปฏิบัติการยูนิกซ์เชิงพาณิชย์เป็นที่แพร่หลายอย่างน่าเศร้า แม้แต่Heirloom Bourneก็ไม่มีข้อผิดพลาดดังนั้น SunOS codebase (ซึ่งเป็น UNIX เชิงพาณิชย์ตัวสุดท้ายที่ใช้ส่ง POSIX ที่ไม่ใช่ POSIX /bin/shและ Heirloom Bourne รุ่นก่อน) ไม่ได้มีปัญหาเช่นกัน
Charles Duffy

1
ฉันไม่สวมหมวกดังนั้นฉันจึงไม่สามารถสัญญาว่าจะโพสต์วิดีโอบน YouTube ที่กินได้หากมีคนเปิดเปลือกหอยที่เผยแพร่บน UNIX เชิงพาณิชย์ด้วยข้อผิดพลาดนี้โพสต์เมื่อปี 1990 ... แต่ถ้าฉันทำเช่นนั้น . :)
Charles Duffy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.