กรณีการใช้งานของ noop [:] ใน bash คืออะไร?


124

ฉันค้นหา noop ใน bash (:) แต่ไม่พบข้อมูลที่ดี วัตถุประสงค์ที่แน่นอนหรือกรณีการใช้งานของตัวดำเนินการนี้คืออะไร?

ฉันลองทำตามแล้วและมันก็ใช้ได้สำหรับฉัน:

[mandy@root]$ a=11
[mandy@root]$ b=20
[mandy@root]$ c=30
[mandy@root]$ echo $a; : echo $b ; echo $c
10
30

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


เข้าไปดูได้ที่stackoverflow.com/questions/7444504/…
Stephane Rouberol

โปรดทราบว่า:บิวท์อินมีอยู่ใน bourne shell และ ksh เช่นเดียวกับ bash
ghoti

ดูเพิ่มเติม:ในtldp.org/LDP/abs/html/special-chars.html
choroba

6
นี่ไม่ใช่คำถามที่แท้จริงได้อย่างไร? ฉันคิดว่ามันเป็นคำถามที่ดีจริงๆ ฉันยังมีประโยชน์สำหรับมัน แต่ฉันไม่สามารถโพสต์คำตอบได้
Steven Lu

คำตอบ:


176

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

while true; do
  echo 'Going on forever'
done

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

while keep_waiting; do
  : # busy-wait
done

:วัน builtin ตลอดทางกลับไปที่ธ อมป์สันเปลือกมันเป็นปัจจุบันในระบบปฏิบัติการยูนิกซ์ v6 :เป็นตัวบ่งชี้ฉลากสำหรับgotoคำสั่งของ Thompson shell ฉลากอาจจะเป็นข้อความใด ๆ เพื่อให้:สองเท่าขึ้นเป็นตัวบ่งชี้ความคิดเห็น (ถ้าไม่มีgoto commentแล้ว: commentอย่างมีประสิทธิภาพความคิดเห็น) เปลือกบอร์นไม่ได้มีแต่เก็บไว้goto:

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

ดูเพิ่มเติมลำไส้ใหญ่ในตัวทำหน้าที่อะไร? .


11
สรุปได้ดี. นอกจากนี้ Bourne เชลล์ดั้งเดิมไม่ได้ใช้#สำหรับความคิดเห็น (หรือ#!/bin/shShebangs) :คำสั่งแนะนำความคิดเห็นและเป็นลางวิบัติโปรแกรมเมอร์ไร้เดียงสาที่ทำกล่องที่ดีของดาวในความคิดเห็นของพวกเขา: ****(หรือแย่ลง: * * *)
Jonathan Leffler

3
@JonathanLeffler: อัปยศกับฉัน แต่ฉันไม่เข้าใจ จะเกิดอะไรขึ้น: * * *?
DevSolar

7
เนื่องจาก:เป็นคำสั่งเชลล์ยังคงต้องประมวลผลอาร์กิวเมนต์ก่อนที่จะค้นพบว่า:ละเว้น ส่วนใหญ่คุณแค่ทำให้เชลล์ทำงานพิเศษในการขยาย*ไปยังรายการไฟล์ในไดเร็กทอรีปัจจุบัน มันจะไม่ส่งผลต่อการทำงานของสคริปต์
chepner

ฉันคิดว่ามันอาจทำให้สคริปต์ของคุณล้มเหลวหากคุณเกิดขึ้นเพื่อรันในไดเร็กทอรีที่มีไฟล์จำนวนมากทำให้การขยาย glob เกินขีดจำกัดความยาวคำสั่ง? บางทีเฉพาะถ้าคุณมีset -eใน อย่างไรก็ตามหวังว่าเราทุกคนจะเห็นด้วยที่จะใช้#:)
Jack O'Connor

2
บางครั้งฉันใช้while : ; do ... ; doneถ้าฉันต้องการลูปที่ไม่มีที่สิ้นสุดที่รวดเร็วและสกปรก (โดยปกติจะมีsleepการวนซ้ำและฉันพิมพ์ Ctrl-C เพื่อฆ่ามัน)
Keith Thompson

21

ฉันใช้มันสำหรับคำสั่ง if เมื่อฉันแสดงความคิดเห็นเกี่ยวกับรหัสทั้งหมด ตัวอย่างเช่นคุณมีการทดสอบ:

if [ "$foo" != "1" ]
then
    echo Success
fi

แต่คุณต้องการแสดงความคิดเห็นชั่วคราวเกี่ยวกับทุกสิ่งที่อยู่ภายใน:

if [ "$foo" != "1" ]
then
    #echo Success
fi

ซึ่งทำให้ bash ให้ข้อผิดพลาดทางไวยากรณ์:

line 4: syntax error near unexpected token `fi'
line 4: `fi'

Bash ต้องไม่มีบล็อกว่าง (WTF) ดังนั้นคุณจึงเพิ่ม no-op:

if [ "$foo" != "1" ]
then
    #echo Success
    :
fi

หรือคุณสามารถใช้ no-op เพื่อแสดงความคิดเห็นในบรรทัด:

if [ "$foo" != "1" ]
then
    : echo Success
fi

12

ถ้าคุณใช้set- eแล้ว|| :เป็นวิธีที่ดีที่จะไม่ออกจากสคริปต์ถ้าเป็นความล้มเหลวที่เกิดขึ้น (มันอย่างชัดเจนทำให้มันผ่าน)


1
ฉันพบว่าสิ่งนี้มีประโยชน์สำหรับแอปพลิเคชันการเรียกเชลล์สคริปต์บางตัว ตัวอย่างเช่น Automator บน Mac จะออกจากข้อผิดพลาดและไม่บอกสาเหตุให้คุณทราบ แต่การใช้|| :หลังจากนั้นจัดการข้อผิดพลาดด้วยตัวคุณเองด้วยexit 0จะช่วยให้คุณสามารถแสดง stdout และ stderr ทั้งหมดไปยังหน้าต่างแอพในระหว่างการดีบัก พิจารณาว่าวิธี{ foo ... 2>&1; } || :ใดง่ายกว่าการสร้างกับดักและ if-thens ที่ซับซ้อนกว่ามาก
Beejor

7

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

#!/bin/sh
# example
verbosity=:                         
while getopts v OPT ; do          
   case $OPT in                  
       v)        
           verbosity=/bin/realpath 
       ;;
       *)
           exit "Cancelled"
       ;;             
   esac                          
done                              

# `$verbosity` always succeeds by default, but does nothing.                              
for i in * ; do                   
  echo $i $($verbosity $i)         
done                              

$ example
   file

$ example -v
   file /home/me/file  

พูดอย่างเคร่งครัดการกำหนดค่าให้กับตัวแปรคือ "ทำอะไรบางอย่าง" ด้วยเหตุนี้จึงไม่ทำให้เกิดข้อผิดพลาดในคำสั่งกรณีของคุณ
qodeninja

@qodeninja: ไม่มีใครอ้างว่าการกำหนดตัวแปร "ไม่ได้ทำอะไรเลย"; ประเด็นคือใน$(verbosity $i)ภายหลัง (และตามเงื่อนไข) ไม่ทำอะไรเลย
Lightness Races ใน Orbit

6

ไม่สนใจ aliasข้อโต้แย้ง

บางครั้งคุณต้องการมีนามแฝงที่ไม่ใช้โต้แย้งใด ๆ คุณสามารถทำได้โดยใช้::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there

ไม่ใช่ผู้โหวตลงคะแนน แต่คำตอบนี้ไม่ได้แสดงให้เห็นถึงวิธีการ:ทำงานน่าเสียดาย ตัวอย่างที่คุณให้มาดูเหมือนซ้ำซ้อน
qodeninja

2
คำถามไม่ใช่วิธีการทำงาน แต่กรณีการใช้งานคืออะไร เป็นความจริงคำตอบของฉันคล้ายกับstackoverflow.com/a/37170755/6320039เล็กน้อย แต่มันไม่ใช่กรณีการใช้งานเดียวกัน ฉันยินดีที่จะปรับปรุงคำตอบของฉัน แต่ฉันไม่รู้จริงๆว่าที่นี่ ..
Ulysse BN

1
นี่เป็นเรื่องขี้ขลาดเล็กน้อยหากคุณต้องการ แต่ก็ยังน่าสนใจและอาจเป็นเคล็ดลับที่ชาญฉลาดที่จะมีในกระเป๋าหลัง
Mike S

2
เช่นเดียวกันกับ#
Zoey Hewll

2
@ZoeyHewll ก็ไม่เชิง เนื่องจากนามแฝงถูกแทนที่ด้วยข้อความก่อนการดำเนินการใด ๆ การใช้นามแฝงเพื่อ#ทำให้ทุกสิ่งที่มาในรูปแบบไวยากรณ์หลังจากในบรรทัดเดียวกันถูกละเว้น นี่คือแตกต่างกันจริงๆ (พิจารณาmy_alias arg ; other_commandหรือตรงกันข้าม my_alias arg1 {BACKSLASH}{NEWLINE} arg2) และอาจจะไม่ได้เป็นสิ่งที่คุณต้องการเช่นนั้นอาจก่อให้เกิดข้อผิดพลาดทางไวยากรณ์ (เดาอะไรwhile my_alias ; do true ; doneหรือwhile true ; do my_alias ; doneจะทำ)
Maëlan

4

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

: << 'EOF'

This part of the script is a commented out

EOF

อย่าลืมที่จะใช้คำพูดรอบEOFเพื่อให้ภายในรหัสใด ๆ $(foo)ไม่ได้รับการประเมินเช่น นอกจากนี้ยังอาจจะมีมูลค่าโดยใช้ชื่อเทอร์มิที่ใช้งานง่ายเหมือนNOTES, หรือSCRATCHPADTODO


เคล็ดลับเด็ด! โดยเฉพาะอย่างยิ่งสำหรับโน้ตและรหัสขูดที่ต้องใช้ "#" หลายสิบตัวเมื่อห่อหุ้มอย่างแน่นหนา ผลข้างเคียงคือไม่เหมือนกับบรรทัดความคิดเห็นตัวเน้นไวยากรณ์บางตัวจะเพิกเฉยต่อการถ่ายทอดทางพันธุกรรมโดยระบายสีให้เหมือนรหัสปกติ (หรืออย่างน้อยก็ข้อความธรรมดา) ซึ่งเหมาะอย่างยิ่งสำหรับการตรวจสอบโค้ดที่แสดงความคิดเห็นหรือช่วยละสายตาจากย่อหน้าด้วยสีนีออนคอมเมนต์ ข้อแม้เพียงอย่างเดียวคือบล็อกดังกล่าวควรระบุว่าเป็นความคิดเห็นในโค้ดที่ใช้ร่วมกันเนื่องจากไวยากรณ์ไม่ซ้ำกันและอาจทำให้เกิดความสับสน จากนั้นอีกครั้งนี่คือเปลือกที่เรากำลังพูดถึงซึ่งอาจทำให้เกิดความสับสนเป็นระบบ
Beejor

3

สองของฉัน

ฝังความคิดเห็น POD

แอปพลิเคชั่นที่ค่อนข้างขี้ขลาด:คือการฝังความคิดเห็น POD ในสคริปต์ทุบตีเพื่อให้สามารถสร้างเพจได้อย่างรวดเร็ว แน่นอนในที่สุดก็จะเขียนสคริปต์ทั้งหมดใน Perl ;-)

การรวมฟังก์ชันรันไทม์

นี่คือรูปแบบรหัสประเภทหนึ่งสำหรับการผูกฟังก์ชันในขณะทำงาน Fi มีฟังก์ชั่นการดีบักเพื่อทำบางสิ่งเฉพาะเมื่อมีการตั้งค่าสถานะบางอย่าง:

#!/bin/bash
# noop-demo.sh 
shopt -s expand_aliases

dbg=${DBG:-''}

function _log_dbg {
    echo >&2 "[DBG] $@"
}

log_dbg_hook=':'

[ "$dbg" ] && log_dbg_hook='_log_dbg'

alias log_dbg=$log_dbg_hook


echo "Testing noop alias..."
log_dbg 'foo' 'bar'

คุณได้รับ:

$ ./noop-demo.sh 
Testing noop alias...
$ DBG=1 ./noop-demo.sh 
Testing noop alias...
[DBG] foo bar

2

บางครั้งคำสั่ง no-op อาจทำให้โค้ดของคุณอ่านง่ายขึ้น

นั่นอาจเป็นเรื่องของความคิดเห็น แต่นี่คือตัวอย่าง สมมติว่าคุณได้สร้างฟังก์ชันที่ใช้งานได้โดยใช้ unix สองพา ธ จะคำนวณ 'เส้นทางการเปลี่ยนแปลง' ที่จำเป็นในการซีดีจากเส้นทางหนึ่งไปยังอีกเส้นทางหนึ่ง คุณวางข้อ จำกัด ในฟังก์ชันของคุณว่าพา ธ ทั้งสองต้องขึ้นต้นด้วย '/' หรือทั้งคู่ต้องไม่

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        true      # continue with function
    else
        return 1  # Skip function.
    fi

นักพัฒนาบางรายต้องการลบ no-op ออก แต่นั่นหมายถึงการลบล้างเงื่อนไข:

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" != / || "$fromC" == / ]] && [[ "$toC" == / || "$fromC" != / ]]
    then
        return 1  # Skip function.
    fi

ตอนนี้ - ในความคิดของฉัน - มันไม่ชัดเจนนักจาก if-clause เงื่อนไขที่คุณต้องการข้ามการทำฟังก์ชัน หากต้องการกำจัด no-op และทำอย่างชัดเจนคุณต้องย้าย if-clause ออกจากฟังก์ชัน:

    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        cdPath=$(chgPath pathA pathB)   # (we moved the conditional outside)

ดูดีขึ้น แต่หลายครั้งเราทำไม่ได้ เราต้องการให้ทำการตรวจสอบภายในฟังก์ชัน

แล้วสิ่งนี้เกิดขึ้นบ่อยแค่ไหน? ไม่บ่อยมาก. อาจจะปีละครั้งหรือสองครั้ง มันเกิดขึ้นบ่อยพอที่คุณควรตระหนักถึงมัน ฉันไม่อายที่จะใช้มันเมื่อฉันคิดว่ามันช่วยเพิ่มความสามารถในการอ่านรหัสของฉัน (โดยไม่คำนึงถึงภาษา)


3
หากคุณกำลังตอบคำถามเกี่ยวกับจุดประสงค์:คุณควรใช้:ไม่ใช่trueในคำตอบ ที่กล่าวว่าวิธีที่ง่ายที่สุดในการแก้เงื่อนไขที่นี่คือการใช้[[ ... ]]คำสั่งและคำนำหน้าด้วย:! if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then
chepner

1
อ่าดีมากและฉันควรโหวตคำตอบของฉัน อืม .... ฉันยังคงนึกถึงตัวอย่างที่ต้องใช้ no-op clause นี่คือสิ่งที่ดีที่สุดที่ฉันคิดขึ้นมา
Bitdiot

มันจริงๆเพียงแค่เก็บไว้หลังเข้ากันได้แม้จะ: ${...=...}เป็น arguably true ${...=...}น้อยที่น่าอึดอัดใจมองกว่า บางคนอาจชอบที่while :; doจะมีwhile true; doความตึงเครียดเช่นกัน
chepner

2

ค่อนข้างเกี่ยวข้องกับคำตอบนี้ฉันพบว่า no-op นี้ค่อนข้างสะดวกในการแฮ็กสคริปต์หลายภาษา ตัวอย่างเช่นนี่คือความคิดเห็นที่ถูกต้องทั้งสำหรับ bash และ vimscript:

":" #    this is a comment
":" #    in bash, ‘:’ is a no-op and ‘#’ starts a comment line
":" #    in vimscript, ‘"’ starts a comment line

แน่นอนว่าเราอาจใช้trueเช่นกัน แต่:การเป็นเครื่องหมายวรรคตอนไม่ใช่คำภาษาอังกฤษที่ไม่เกี่ยวข้องทำให้ชัดเจนว่าเป็นโทเค็นไวยากรณ์


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

ในสถานการณ์เช่นนี้การรวมสคริปต์ทั้งสองไว้ในไฟล์ polyglot ไฟล์เดียวจะหลีกเลี่ยงงานใด ๆ ในXการกำหนดเส้นทางไปยังY(เป็นเพียงแค่"$0") ที่สำคัญทำให้สะดวกในการเคลื่อนย้ายหรือแจกจ่ายโปรแกรม

  • ตัวอย่างทั่วไป มีปัญหาที่รู้จักกันดีและมีมายาวนานเกี่ยวกับ Shebangs: ระบบส่วนใหญ่ (รวมถึง Linux และ Cygwin) อนุญาตให้ส่งอาร์กิวเมนต์ไปยังล่ามได้เพียงข้อเดียว Shebang ต่อไปนี้:

    #!/usr/bin/env interpreter --load-libA --load-libB

    จะเริ่มคำสั่งต่อไปนี้:

    /usr/bin/env "interpreter --load-libA --load-libB" "/path/to/script"

    และไม่ได้ตั้งใจ:

    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    ดังนั้นคุณจะต้องเขียนสคริปต์ wrapper เช่น:

    #!/usr/bin/env sh
    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    นี่คือจุดที่ polyglossia เข้าสู่เวที

  • ตัวอย่างที่เฉพาะเจาะจงมากขึ้น ครั้งหนึ่งฉันเคยเขียนสคริปต์ทุบตีซึ่งเรียกเป็นกลุ่ม --cmd "arbitrary vimscript command here"ฉันต้องการที่จะให้เป็นกลุ่มการตั้งค่าเพิ่มเติมซึ่งสามารถทำได้ด้วยตัวเลือก อย่างไรก็ตามการตั้งค่านั้นมีความสำคัญมากดังนั้นการแทรกในสตริงจะแย่มาก (ถ้าเป็นไปได้) ดังนั้นทางออกที่ดีกว่าคือเขียนเป็นextensoในไฟล์กำหนดค่าบางไฟล์จากนั้นให้ Vim อ่านไฟล์นั้นด้วย-S "/path/to/file". ดังนั้นฉันจึงลงเอยด้วยไฟล์ polyglot bash / vimscript



0

สมมติว่าคุณมีคำสั่งที่คุณต้องการเชื่อมโยงไปสู่ความสำเร็จของอีกฝ่าย:

cmd="some command..."
$cmd
[ $? -eq 0 ] && some-other-command

แต่ตอนนี้คุณต้องการดำเนินการคำสั่งตามเงื่อนไขและคุณต้องการแสดงคำสั่งที่จะดำเนินการ (dry-run):

cmd="some command..."
[ ! -z "$DEBUG" ] && echo $cmd
[ -z "$NOEXEC" ] && $cmd
[ $? -eq 0 ] && {
    cmd="some-other-command"
    [ ! -z "$DEBUG" ] && echo $cmd
    [ -z "$NOEXEC" ] && $cmd
}

ดังนั้นหากคุณตั้งค่า DEBUG และ NOEXEC คำสั่งที่สองจะไม่ปรากฏขึ้น นี่เป็นเพราะคำสั่งแรกไม่เคยดำเนินการ (เนื่องจาก NOEXEC ไม่ว่างเปล่า) แต่การประเมินความจริงนั้นทำให้คุณได้รับผลตอบแทนเป็น 1 ซึ่งหมายความว่าคำสั่งรองจะไม่ดำเนินการ (แต่คุณต้องการให้ใช้เพราะเป็นการทำงานแบบแห้ง) ดังนั้นในการแก้ไขปัญหานี้คุณสามารถรีเซ็ตค่าทางออกที่เหลือบนสแต็กโดยใช้ noop:

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