ยูทิลิตี้ของคำสั่ง: ในเชลล์สคริปต์คืออะไรโดยไม่ได้ทำอะไรเลย?


27

ในคำตอบสำหรับคำถามนี้เกี่ยวกับความคิดเห็นในการเขียนสคริปต์เชลล์จะมีการระบุว่า:คำสั่ง null เป็นคำสั่งที่ไม่ทำอะไรเลยอย่างชัดเจน (แต่ไม่ควรใช้สำหรับความคิดเห็น)

สิ่งที่เป็นประโยชน์ของคำสั่งที่ไม่ทำอะไรเลยอย่างแน่นอน?


1
ดูเพิ่มเติมวัตถุประสงค์อะไรไม่ลำไส้ใหญ่ในตัวให้บริการ
jw013

2
ดูคำถามนี้ซึ่งมีคำตอบที่ดียิ่งขึ้นที่นี่กล่าว:คือต้องใช้แบบบิวด์อินในขณะที่trueไม่ใช่ซึ่งมีผลกับขอบเขตของตัวแปร
Old Pro

2
มันชัดเจนไม่ได้ทำอะไรอย่างสง่างาม
mikeserv

คำตอบ:


19

ปกติฉันใช้เป็นtrueลูป ฉันคิดว่ามันชัดเจนกว่า:

while true; do
    ...
done

ที่เดียวที่ฉันพบว่า:มีประโยชน์จริงๆคือในกรณีที่เป็นงบถ้าคุณต้องการจับคู่บางอย่าง แต่ไม่ต้องการทำอะไรจริง ๆ ตัวอย่างเช่น:

case $answer in
    ([Yy]*) : ok ;;
    (*)     echo "stop."; exit 1 ;;
esac

3
ฉันเห็นด้วย. trueสำหรับเงื่อนไข:สำหรับ NOP
jw013

1
case a in a ) ;; esacทุบตียอมรับ มีกระสุนบ้างไหมที่ไม่ยอมรับสิ่งนี้?
Kaz

@Kaz: ตามไวยากรณ์ POSIX เปลือก , case ${var} in value);; *) do_something;; esacเป็นที่ยอมรับ :คำสั่งไม่จำเป็นสำหรับกรณีที่ว่างเปล่า
Richard Hansen

12

ในขั้นต้นมันถูกใช้เพื่อตรวจสอบว่ามันเป็นโปรแกรมเชลล์เป้าหมายเมื่อเทียบกับโปรแกรมที่คอมไพล์ด้วย C นี่คือก่อนที่ shebang และภาษาสคริปต์หลายภาษา (csh, perl) คุณยังสามารถเรียกใช้สคริปต์โดยเริ่มจาก::

$ echo : > /tmp/xyzzy
$ chmod +x /tmp/xyzzy
$ ./xyzzy

โดยทั่วไปจะเรียกใช้สคริปต์กับ $SHELL (หรือ/bin/sh)

ตั้งแต่นั้นมาการใช้หลักคือการประเมินข้อโต้แย้ง ฉันยังใช้:

: ${EDITOR:=vim}

เพื่อตั้งค่าเริ่มต้นในสคริปต์


11

: มีประโยชน์สำหรับการเขียนลูปที่ต้องถูกยกเลิกจากภายใน

while :
do
    ...stuff...
done

นี้จะทำงานตลอดไปเว้นแต่breakหรือexitที่เรียกว่าหรือเปลือกได้รับสัญญาณยุติ


3
ฉันรู้สึกว่าwhile true; do ...; doneสื่อสารความตั้งใจกับผู้อ่านดีกว่าwhile :; do ...; done
ริชาร์ดแฮนเซน

9

เมื่อคุณต้องการคำสั่ง "เว้นแต่" ในการเขียนสคริปต์เชลล์คุณอาจใช้เงื่อนไข "ไม่" ซึ่งสามารถดูโง่ ๆ สำหรับการทดสอบบางอย่างหรือคุณใช้ ':' ในประโยคจริงด้วยรหัสจริงในเท็จ - ประโยค

if [ some-exotic-condition ]
then
    :
else
    # Real code here
fi

"สภาพที่แปลกใหม่" อาจเป็นสิ่งที่คุณไม่ต้องการที่จะปฏิเสธหรือนั่นชัดเจนกว่าถ้าคุณไม่ใช้ "ตรรกะเชิงลบ"


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

2
ฉันไม่เห็นว่าการติดอยู่!ตรงหน้า[ some-exotic-condition ]เป็นเรื่องโง่ แต่สิ่งที่ฟุ่มเฟือย: elseหลังจากนั้นไม่ใช่เรื่องไร้สาระ
Kaz

@Kaz มีจุดดี ขอให้ระลึกไว้เสมอว่าการมากับสภาวะแปลกใหม่นั้นยากที่สุด หากคุณต้องคัดค้านทั้งหมดนั่นเป็นสิ่งหนึ่ง แต่ก็อาจทำให้สภาพไม่ชัดเจน ทำ '!' ลบล้างเงื่อนไขทั้งหมดหรือแค่เทอมแรก? ดีที่สุดที่จะมี ':' ประโยคจริงบางครั้ง
Bruce Ediger

!โทเค็ขัดแย้งองค์ประกอบท่อคำสั่งทั้งหมด หรือwhile ! grep ... ; do ... done if ! [ ... ] ; then ... fiมันเป็นพื้นนอกtest/[]ไวยากรณ์ ดู: pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Kaz

5

ฉันเคยใช้สิ่งนี้นอกเหนือจาก # อักขระสำหรับใส่เครื่องหมายบรรทัดชั่วคราวในสถานการณ์ที่การคอมเม้นต์บรรทัดทำให้เกิดข้อผิดพลาดทางไวยากรณ์เนื่องจากข้อบกพร่องในไวยากรณ์ไวยากรณ์ไม่อนุญาตให้มีลำดับของคำสั่งที่ว่างเปล่า :

if condition ; then
    :# temporarily commented out command
fi

หากไม่มี: เรามีลำดับคำสั่งที่หายไปซึ่งเป็นข้อผิดพลาดทางไวยากรณ์


4

มีสองกรณีที่ฉันพบว่า:มีประโยชน์:

การกำหนดตัวแปรเริ่มต้น

#!/bin/sh

# set VAR to "default value" if not already set in the environment
: "${VAR=default value}"

# print the value of the VAR variable.  Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR}"

นี่เป็นวิธีที่สะดวกในการอนุญาตให้ผู้ใช้เชลล์สคริปต์ของคุณแทนที่การตั้งค่าโดยไม่ต้องแก้ไขสคริปต์ (อย่างไรก็ตามอาร์กิวเมนต์บรรทัดคำสั่งจะดีกว่าเพราะคุณไม่เสี่ยงต่อพฤติกรรมที่ไม่คาดคิดหากผู้ใช้มีตัวแปรที่คุณใช้ในสภาพแวดล้อมที่ส่งออกโดยบังเอิญ) นี่คือวิธีที่ผู้ใช้จะแทนที่การตั้งค่า:

VAR="other value" ./script

${VAR=value}ไวยากรณ์พูดกับชุดVARไปvalueถ้าVARไม่ได้ถูกกำหนดไว้แล้วจากนั้นขยายไปเป็นค่าของตัวแปร เนื่องจากเราไม่สนใจเกี่ยวกับคุณค่าของตัวแปรเลยมันจึงถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังคำสั่ง no-op :เพื่อละทิ้งมัน

แม้ว่าจะ:เป็นคำสั่ง no-op แต่การขยายจะดำเนินการโดยเชลล์ (ไม่ใช่:คำสั่ง!) ก่อนที่จะรัน:คำสั่งเพื่อให้การกำหนดตัวแปรยังคงเกิดขึ้น (ถ้ามี)

มันก็จะเป็นที่ยอมรับได้ที่จะใช้trueหรือคำสั่งอื่น ๆ แทน:แต่รหัสกลายเป็นยากต่อการอ่านเพราะความตั้งใจที่ชัดเจนน้อยลง

สคริปต์ต่อไปนี้ยังสามารถใช้งานได้:

#!/bin/sh

# print the value of the VAR variable.  Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR=default value}"

แต่ข้างต้นนั้นยากที่จะรักษา หากมีการ${VAR}เพิ่มบรรทัดที่ใช้อยู่เหนือprintfบรรทัดนั้นจะต้องย้ายส่วนขยายการมอบหมายเริ่มต้น หากผู้พัฒนาลืมที่จะย้ายการบ้านนั้นจะมีการแนะนำบั๊ก

สิ่งที่ต้องใส่ในบล็อกที่มีเงื่อนไขว่างเปล่า

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

if some_condition; then
    # todo:  implement this block of code; for now do nothing.
    # the colon below is a no-op to prevent syntax errors
    :
fi

บางคนยืนยันว่าการมีifบล็อกว่างเปล่าจริง ๆสามารถทำให้อ่านรหัสได้ง่ายกว่าการปฏิเสธการทดสอบ ตัวอย่างเช่น:

if [ -f foo ] && bar || baz; then
    :
else
    do_something_here
fi

อ่านง่ายกว่า:

if ! [ -f foo ] || ! bar && ! baz; then
    do_something_here
fi

อย่างไรก็ตามฉันเชื่อว่ามีวิธีการอื่น ๆ ที่ดีกว่าบล็อกจริงที่ว่างเปล่า:

  1. ใส่เงื่อนไขในฟังก์ชั่น:

    exotic_condition() { [ -f foo ] && bar || baz; }
    
    if ! exotic_condition; then
        do_something_here
    fi
  2. ใส่เงื่อนไขไว้ในเครื่องหมายปีกกา (หรือวงเล็บ แต่วงเล็บจะวางกระบวนการย่อยและการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นกับสภาพแวดล้อมภายใน subshell จะไม่ปรากฏภายนอก subshell) ก่อนที่จะปฏิเสธ:

    if ! { [ -f foo ] && bar || baz; } then
        do_something_here
    fi
  3. ใช้||แทนif:

    [ -f foo ] && bar || baz || {
        do_something_here
    }

    ฉันชอบวิธีนี้เมื่อปฏิกิริยาตอบสนองนั้นง่าย ๆ แบบหนึ่งซับเช่นเงื่อนไขในการยืนยัน:

    log() { printf '%s\n' "$*"; }
    error() { log "ERROR: $*" >&2; }
    fatal() { error "$@"; exit 1; }
    
    [ -f foo ] && bar || baz || fatal "condition not met"

1

ในเชลล์พรีเบิร์นเก่าในเวอร์ชันโบราณของ UNIX :คำสั่งนี้มีจุดประสงค์เพื่อระบุเลเบลสำหรับgoto(เป็นคำสั่งแยกต่างหากซึ่งทำให้อินพุตอยู่ในตำแหน่งที่พบเลเบลดังนั้นป้ายกำกับไม่สามารถเป็นไวยากรณ์แยกต่างหากที่ shell รู้เกี่ยวกับ. ifยังเป็นคำสั่งที่แยกต่างหาก.) ในไม่ช้ามันก็ถูกใช้สำหรับความคิดเห็นก่อนที่จะมีไวยากรณ์ของความคิดเห็น ( #ใช้สำหรับ backspace) และวันนี้ก็ใกล้เข้ากันได้มากเท่ากับอะไร


0

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


มันจะไม่เป็นความคิดเห็นอย่างแน่นอนเพราะ: echo write this line > myfileจะยังคงสร้างไฟล์ว่างเปล่า
Arcege

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