GNU ค้นหาและปิดบัง {} สำหรับบางเชลล์ - อันไหน?


34

หน้าคนสำหรับ GNU ค้นหารัฐ:

-exec command ;
    [...] The  string `{}'  is  replaced  by the current 
    file name being processed everywhere it occurs in the 
    arguments to the command, not just in arguments where 
    it is alone, as in some  versions  of  find.
    Both  of  these  constructions might need to be escaped 
    (with a `\') or quoted to protect them from expansion 
    by the shell. 

นั่นคือจากคนสู่find(GNU findutils) 4.4.2

ตอนนี้ฉันทดสอบสิ่งนี้ด้วย bash และ dash และทั้งคู่ไม่จำเป็นต้อง{}ถูกหลอกลวง นี่คือการทดสอบอย่างง่าย:

find /etc -name "hosts" -exec md5sum {} \; 

มีเปลือกหอยหรือไม่ซึ่งฉันจำเป็นต้องปิดบังเหล็กดัด? โปรดทราบว่ามันไม่ได้ขึ้นอยู่กับว่าไฟล์ที่พบมีช่องว่าง (เรียกใช้จาก bash):

find ~ -maxdepth 1 -type d -name "U*" -exec ls -d {} \; 
/home/stefan/Ubuntu One

การเปลี่ยนแปลงนี้หากไฟล์ที่พบถูกส่งผ่านไปยัง subshell:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d {}' \; 
ls: cannot access /home/stefan/Ubuntu: No such file or directory
ls: cannot access One: No such file or directory

ซึ่งสามารถแก้ไขได้โดย:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "$0"' {} \;

ตรงกันข้ามกับ:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "{}"' \; 
/home/stefan/Ubuntu One

แต่นั่นไม่ใช่สิ่งที่หน้าคนพูดถึงใช่มั้ย ดังนั้นเปลือกหอยชนิดใดที่ปฏิบัติ{}แตกต่างกันไป?


@Volker Siegel: การแก้ไขของคุณอาจเกิดขึ้นโดยเจตนา แต่ฉันตรวจสอบคู่มือการค้นหาปัจจุบันของฉันและการแก้ไขของคุณผิดคุณยังพลาดโอกาสที่จะสรุปการแก้ไขของคุณซึ่งเป็นนิสัยที่ไม่ดีดังนั้นฉันจึงย้อนกลับการเปลี่ยนแปลงของคุณ ขอโทษนะเพื่อน
ผู้ใช้ที่ไม่รู้จัก

โอ้ขอบคุณสำหรับการม้วนกลับถ้ามันทำลายบางสิ่งบางอย่าง ฉันจำได้ว่าคอมไพเลอร์ข้อความที่สร้างรูปแบบหน้าคนที่ใช้ในการสร้าง "คำพูดพิมพ์" เหล่านี้เป็นสิ่งประดิษฐ์ของคำนิยามการแสดงผล นอกจากนี้ยังสามารถแสดงผลเป็น TeX เพื่อการจัดรูปแบบงานพิมพ์ที่สมบูรณ์แบบ ฉันสังเกตเห็นคำพูดเพราะพวกเขาสับสนการเน้นไวยากรณ์ ฉันสันนิษฐานว่าคำพูดแรกที่ผิดปกตินั้นผิดเมื่อใช้ในโค้ดและยังถือว่าอยู่ โปรดทราบว่าการเปลี่ยนแปลงทั้งสองนี้เป็นข้อความคำอธิบายไม่ใช่รหัส ดังนั้นถ้าเราเห็นว่ามันเป็นตัวอักษร - การแสดงผลที่สวยงาม - มันถูกต้อง
Volker Siegel

ดูเหมือนว่าในการแสดงผลข้อมูลการเสนอราคาเดียวกันจะใช้ทั้งสองด้าน ไม่ต้องสงสัยเลยว่าการเปลี่ยนแปลงของฉันทำให้แตกต่างจากของจริงคุณพูดถูก แต่มันทำให้เกิดปัญหาใด ๆ ? (ฉันถือว่าข้อความแรกเป็นข้อผิดพลาดในนิยามการเรนเดอร์แมนมันเป็นเพียงกรณีของวิชาการพิมพ์เท่าที่ฉันรู้)
Volker Siegel

ฉันเพิ่งตรวจสอบ: `{} 'ไม่ทำงานบนบรรทัดคำสั่ง ถูกต้องในทางเทคนิค แต่ฉันไม่ชอบที่ผู้ใช้ที่ไม่สงสัยจะได้รับข้อผิดพลาดแปลก ๆ เมื่อเขาพยายามคัดลอกและวาง
Volker Siegel

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

คำตอบ:


26

สรุป : หากมีกระสุนที่ขยายออกไป{}มันเป็นสิ่งที่เก่าแก่จริงๆในตอนนี้

ในบอร์นเชลล์และ POSIX สอดคล้องหอยวงเล็บ ( {และ}) เป็นตัวละครสามัญ (เหมือน(และ)ที่คั่นคำเหมือน;และ&และ[และ]ที่มีการ globbing ตัวอักษร) สตริงทั้งหมดต่อไปนี้ควรถูกพิมพ์อย่างแท้จริง:

$ echo { } {} {foo,bar} {1..3}
{ } {} {foo,bar} {1..3}

คำที่ประกอบด้วยวงเล็บปีกกาเดียวคือคำที่สงวนไว้ซึ่งเป็นคำที่พิเศษถ้ามันเป็นคำแรกของคำสั่ง

Ksh ใช้การขยายรั้งเป็นส่วนขยายที่เข้ากันไม่ได้กับเชลล์เป้าหมาย set +Bนี้สามารถปิดได้ด้วย Bash จำลอง ksh ในส่วนนี้ Zsh ดำเนินการขยายตัวด้วยเช่นกัน นั้นก็สามารถปิดได้ด้วยset +Iหรือหรือsetopt ignore_braces emulate shไม่มีเปลือกหอยเหล่านี้ขยายตัว{}ในกรณีใด ๆ แม้ในขณะที่มันเป็นย่อยของคำ (เช่นfoo{}bar) เนื่องจากการใช้งานร่วมกันในข้อโต้แย้งและfindxargs

Single Unix v2ตั้งข้อสังเกตว่า

ในบางระบบที่ผ่านมาการจัดฟันแบบหยิกจะถูกใช้เป็นตัวควบคุม เพื่อช่วยในกิจกรรมการกำหนดมาตรฐานในอนาคตแอปพลิเคชันแบบพกพาควรหลีกเลี่ยงการใช้เครื่องหมายปีกกาที่ไม่มีเครื่องหมายเพื่อแสดงตัวอักขระ เป็นไปได้ว่าเป็นรุ่นอนาคตของมาตรฐาน ISO / IEC 9945-2: 1993 มาตรฐานอาจจำเป็นต้องให้{และ}ได้รับการรักษาเป็นรายบุคคลเป็นผู้ประกอบการควบคุมแม้ว่าโทเค็น{}อาจจะได้รับการยกเว้นกรณีพิเศษจากนี้เพราะมักจะที่ใช้find {}สร้าง

บันทึกย่อนี้ลดลงในรุ่นมาตรฐานที่ตามมา ตัวอย่างสำหรับfindมีการใช้ unquoted ของ{}เช่นเดียวกับตัวอย่างสำหรับ xargsอาจมีบอร์นเชลล์เชิงประวัติศาสตร์ที่{}จะต้องอ้างอิง แต่พวกเขาจะเป็นระบบดั้งเดิมที่เก่าแก่ในตอนนี้

การใช้งาน csh ที่ฉันมีอยู่ในมือ (OpenBSD 4.7, BSD csh บน Debian , tcsh) ทั้งหมดขยาย{foo}ไปfooแต่ทิ้งไว้{}คนเดียว


1
@geekosaur: เพียงส่วนย่อยเล็ก ๆ ของ markdown ทำงานในการแสดงความคิดเห็น ฉันไม่รู้ว่าคุณกำลังพยายามพูดอะไร หากนี่เป็นเรื่องเกี่ยวกับการขยายตัวของ ksh et al อ่านย่อหน้าของฉันที่ขึ้นต้นด้วย“ Ksh ใช้การขยายรั้งเป็นส่วนขยายที่เข้ากันไม่ได้”
Gilles 'หยุดความชั่วร้าย'

2
นั่นคือbash(ดู$BASH_VERSION) การขยายตัวของแผลนั้นมีชีวิตอยู่และดีมาก
geekosaur

2
นั่นคือประเด็น {}ไวยากรณ์ที่เกิดขึ้นในcshแต่{}ขยายไปยังสตริงที่ว่างเปล่า กระสุนที่ใหม่กว่ายอมรับว่ามันไร้สาระ แต่ก็ยังมีของเก่าcshอยู่บ้าง
geekosaur

1
@geekosaur: คุณบอกได้มั้ยว่า csh-version เฉพาะเจาะจงบนแพลตฟอร์มใด ฉันต้องการทดสอบด้วยตัวเอง (ถ้าเป็นไปได้ใน linux) หรือมั่นใจได้ว่าคนทดสอบตัวเอง
ผู้ใช้ที่ไม่รู้จัก

1
@geekosaur {}fooจะขยายไปถึงfooแต่{}จะขยายเป็น{}(ยกเว้นเมื่ออยู่ใน backticks แต่การอ้างอิงจะไม่ช่วย) และได้รับการบันทึกไว้เช่น ฉันตรวจสอบแล้วสำหรับ csh ของ 2BSD (เปิดตัวครั้งแรก), 2.79BSD, 2.8BSD และ 2.11BSD
Stéphane Chazelas

12

{}จำเป็นต้องได้รับการอ้างถึงในรุ่นของfishเปลือกก่อนที่จะ 3.0.0

$ fish -c 'echo find -exec {} \;'
find -exec  ;

และใน rc เชลล์ ( akangaขึ้นอยู่กับrcแต่ไม่ใช่es):

$ rc -c "echo find -exec {} ';'"
line 1: syntax error near '{'

สิ่งเหล่านี้อาจไม่ใช่เชลล์ที่ผู้เขียนของ GNU ค้นหาเอกสารที่มีอยู่ในใจเมื่อพวกเขาเขียนข้อความตั้งแต่fishเปิดตัวครั้งแรกในปี 2005 (ในขณะที่ข้อความนั้นหรือที่คล้ายกันมีอยู่แล้วในปี 1994) และrcไม่ได้เป็นเชลล์ Unix

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

# echo find -exec {} \;
find -exec {} ;

และหน้าคนจาก 2BSD cshระบุไว้อย่างชัดเจน :

ในกรณีพิเศษ `{',`}' และ `{} 'จะถูกส่งผ่านโดยไม่ถูกรบกวน

ดังนั้นฉันจะพบว่ามันแปลกมากถ้ารุ่นต่อมาของ csh หรือ tcsh ภายหลังนั้น

มันอาจเป็นการแก้ไขข้อบกพร่องบางอย่างในบางเวอร์ชั่น ยังคงอยู่ที่ 2BSD csh (นั้นเหมือนกันใน 2.79BSD, 2.8BSD, 2.11BSD):

# csh -x
# echo foo {} bar
echo foo {} bar
foo {} bar
# echo `echo foo {} bar`
echo `echo foo {} bar`
echo foo {} bar
foo  bar

การอ้างอิงไม่ได้ช่วย:

# echo `echo foo '{}' bar`
echo `echo foo '{}' bar`
echo foo {} bar
foo  bar

คุณสามารถอ้างอิงการทดแทนคำสั่งทั้งหมด:

# echo "`echo foo {} bar`"
echo `echo foo {} bar`
echo foo {} bar
foo {} bar

แต่นั่นคือการส่งผ่านอาร์กิวเมนต์หนึ่งไปยังเสียงสะท้อนภายนอก

ในcshหรือtcshคุณจะต้องพูด{}เมื่อไม่ได้อยู่ในตัวของมันเองเหมือนใน:

find . -name '*.txt' -type f -exec cp {} '{}.back' \;

(แม้ว่าfindการใช้งานประเภทนั้นจะไม่สามารถพกพาได้เนื่องจากบางfindตัวจะขยายได้{}เมื่ออยู่ในตัวของมันเอง)


1

ในคำว่าcsh. bashและเชลล์สมัยใหม่อื่น ๆ ยอมรับว่าผู้ใช้อาจไม่ได้ขอให้มีการขยายรั้งว่าง (สมัยใหม่cshเป็นจริงtcshและอาจจัดการอย่าง{}sanely โดยขณะนี้)


ยินดีที่ได้ฟัง แต่ฉันขอตรงกันข้ามซึ่งเป็นเปลือกที่ไม่ได้จัดการอย่างถูกต้อง
ผู้ใช้ไม่รู้จัก

คุณกำลังสมมติว่าคนที่จะไปในและฉีกออกหน้าการอ้างอิงข้อมูลที่จะเสริม quoting เพียงเพราะระบบ Linux cshทุกคนมีที่ใหม่กว่า ไม่ใช่ทุกคนที่เรียกใช้ยูทิลิตี้ GNU บน Linux; ในความเป็นจริงมันค่อนข้างทั่วไปในการติดตั้งบนระบบ Unix เชิงพาณิชย์รุ่นเก่าที่มีคำสั่งบันเดิล จำกัด (การแทนที่cshระบบเหล่านี้มีโอกาสน้อยกว่าเนื่องจากสคริปต์ของระบบอาจอาศัยความแปลกประหลาดของต้นฉบับcshและแม้ว่าผู้ใช้ทุกคนจะถูกกำหนดค่าให้ใช้รุ่นที่ใหม่กว่าcshนั้นrootจะต้องคงรุ่นที่รวมไว้ด้วย)
geekosaur

2
ไม่ฉันกำลังโต้เถียงกับผู้ชายคนหนึ่งที่บอกว่าในวิกิของเราเราควรให้คำแนะนำในการใช้ "{}" ตลอดเวลาเพราะหน้าคนพูดอย่างนั้น และตอนนี้ฉันอยากรู้ว่ามีเชลล์ที่ฉันไม่ได้ใช้หรือไม่เช่น ksh, zsh, tcsh, csh หรือ XYZsh ที่ฉันไม่รู้ซึ่งการเรียกร้องนี้เป็นจริงหรือไม่ ไม่มี หากมีเชลล์สำหรับ Unixes ที่แตกต่างกันซึ่งต้องการ "{}" นั่นจะเป็นคำอธิบายที่ดีว่าทำไมประโยคยังอยู่ใน man page แต่ไม่เหมาะสำหรับคำแนะนำสำหรับผู้เริ่มต้น linux
ผู้ใช้ไม่รู้จัก

ตอนนี้ฉันสงสัยว่าถ้าpdkshทำสิ่งที่ถูกต้องหรือไม่ ... แม้ว่าคำตอบที่ถูกต้องสำหรับคำตอบนั้นน่าจะเป็น "ของจริงkshคือ FOSS ทุกวันนี้"
geekosaur

4
Pdksh เช่น mksh, ksh93, bash และ zsh จะขยายเครื่องหมายวงเล็บเมื่อมีเครื่องหมายจุลภาคคั่นระหว่าง (หรือ..สำหรับ ksh93, bash และ zsh) มีเพียง (t) csh เท่านั้นที่ขยายออก{foo}ไปfooและแม้จะ{}อยู่คนเดียว (อย่างน้อยก็ใน BSD ล่าสุด)
Gilles 'หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.