การใช้ตัวเชื่อมต่อหลังจากค้นหาคำสั่ง


10

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

$ pwd
/data/data/com.termux/files/home/test/test1/test4
$ ls
xaa  xab
$ find . -name xac && echo 'found'
found
$ find . -name xaa && echo 'found'
./xaa
found

คำตอบ:


18

คุณสามารถfindพิมพ์ตัวเองfound:

find . -name xac -printf "found\n" -quit

-quitจะทำให้find ลาออกหลังจากการแข่งขันครั้งแรกเพื่อให้foundมีการพิมพ์เพียงครั้งเดียวที่มากที่สุด

ในหัวข้อที่คล้ายกันบน Unix & Linux ( ทำให้การค้นหาล้มเหลวเมื่อไม่พบสิ่งใด ) ฉันเคยgrep -qzส่งคืนสถานะทางออกที่ไม่ใช่ศูนย์หากfindไม่พบสิ่งใด:

find /some/path -print0 -quit | grep -qz .

ซึ่งคุณสามารถใช้เพื่อสร้างคำสั่งผสมโดยใช้&&หรือif:

find /some/path -print0 -quit | grep -qz . && echo found

ฉันต้องจ้องที่ชั่วขณะนี้ /some/pathบอกให้ค้นหาว่าจะเริ่มมองที่ใด แต่ไม่มีอะไรบอกได้ว่าจะมองหาอะไร เหมือนกันในคำตอบที่เชื่อมโยงของคุณ find /some/path -name xac -print0 -quit | grep -qz . && echo foundสิ่งที่ไม่ทำงานสำหรับฉันก็คือ ฉันพลาดอะไรไปหรือเปล่า?
โจ

@ Joe -print0 -quitสิ่งที่สำคัญที่นี่เป็น สิ่งที่คุณใส่ก่อนหน้านั้นขึ้นอยู่กับสิ่งที่คุณต้องการค้นหา ฉันเลือกที่จะไม่ใช้สิ่งนี้
muru

13

คำตอบของ muruนั้นเหมาะสมและเหมาะสมกับกรณีที่เราต้องการพิมพ์บางอย่างหากพบไฟล์ สำหรับกรณีทั่วไปเมื่อเราต้องการรันคำสั่งภายนอกเช่นechoเราสามารถใช้การ-execตั้งค่าสถานะ

$ find . -name 'xac' -exec echo "I found " {} \; -quit             
I found  ./xac

{}ส่วนหนึ่งผ่านชื่อไฟล์คำสั่งระหว่าง-execและ\;เป็นข้อโต้แย้ง หมายเหตุ\ก่อน;- ป้องกันไม่ให้มันจากเปลือกตีความมันในการปิดเปลือกหมายอัฒภาคปลายคำสั่ง แต่เมื่อหนีออกมาด้วยการเฉือนเปลือกจะรักษามันเป็นข้อความ literall จะถูกส่งผ่านไปยังfindคำสั่งและคำสั่งที่จะหามันทำหน้าที่เป็นปิด-execการขัดแย้งธง


สำหรับการสร้าง conditionals ของif found do this; else do thatsort เราสามารถใช้ประโยชน์จาก substition คำสั่ง$()และtestcommand (aka [):

$ [ "x$(find . -name 'noexist' -print -quit)" != "x" ] && echo "found" || echo "not found"                                                                                              
not found

$ [ "x$(find . -name 'xac' -print -quit)" != "x" ] && echo "found" || echo "not found"                                                                                                  
found

พูดถึงความคิดเห็นของแดน

แดนในความคิดเห็นที่ถาม:

จะไม่สะท้อน "ฉันพบ {}" ดีกว่าเสียงสะท้อน "ฉันพบ" {} หรือไม่ อาจเป็นเพราะเสียงสะท้อนได้ดี แต่ถ้ามีคนคัดลอกคำสั่งและแทนที่เสียงสะท้อนด้วยคำสั่งอื่นพวกเขาอาจมีปัญหา

มาทำความเข้าใจปัญหาก่อนกัน โดยปกติแล้วใน shells จะมีแนวคิดเกี่ยวกับการแบ่งคำซึ่งหมายถึงตัวแปรที่ไม่ได้กล่าวถึงและพารามิเตอร์ตำแหน่งจะถูกขยายและถือเป็นรายการแยกต่างหาก ตัวอย่างเช่นถ้าคุณมีตัวแปรvarและจะมีhello worldข้อความเมื่อคุณทำtouch $varเชลล์จะทำลายมันลงเป็นสองรายการแยกต่างหากhelloและworldและtouchจะเข้าใจว่าราวกับว่าคุณกำลังพยายามที่จะสร้าง 2 ไฟล์แยกเป็นสัดส่วน ถ้าคุณทำtouch "$var"แล้วเปลือกจะถือว่าhello worldเป็นหนึ่งหน่วยและtouchจะสร้างหนึ่งไฟล์เท่านั้น สิ่งสำคัญคือต้องเข้าใจว่าสิ่งนี้เกิดขึ้นเพียงเพราะเปลือกทำงานอย่างไร

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

$ touch with$'\t'tab.txt with$' 'space.txt with$'\n'newline.txt

$ find -type f -exec stat -c "%F" {} \; -print                                                                                                                         
regular empty file
./with?newline.txt
regular empty file
./with space.txt
regular empty file
./with?tab.txt

ดังที่คุณเห็นstatได้รับชื่อไฟล์ยากfindๆ ซึ่งเป็นหนึ่งในเหตุผลหลักที่แนะนำให้ใช้ในสคริปต์แบบพกพาและมีประโยชน์อย่างยิ่งเมื่อคุณสำรวจทรีไดเรกทอรีและต้องการทำบางสิ่งบางอย่างกับชื่อไฟล์ที่อาจมี ตัวอักษรพิเศษในพวกเขา findดังนั้นจึงไม่จำเป็นที่จะพูดวงเล็บปีกกาสำหรับคำสั่งดำเนินการใน

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

$ find -type f -exec bash -c "stat {}" sh \;   
stat: cannot stat './with': No such file or directory
sh: line 1: newline.txt: command not found
stat: cannot stat './with': No such file or directory
stat: cannot stat 'space.txt': No such file or directory
stat: cannot stat './with': No such file or directory
stat: cannot stat 'tab.txt': No such file or directory

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

$ find -type f -exec bash -c "stat -c '%F' '{}'" sh \;                                                                                                                 
regular empty file
regular empty file
regular empty file

จะecho "I found {}"ดีกว่าecho "I found " {}ไหม อาจเป็นเพราะเสียงสะท้อนได้ดี แต่ถ้ามีคนคัดลอกคำสั่งและแทนที่เสียงสะท้อนด้วยคำสั่งอื่นพวกเขาอาจมีปัญหา
ด่าน

@Dan หัวข้อนี้ยาวเกินไปที่จะพูดคุยในความคิดเห็นดังนั้นฉันจึงแก้ไขคำตอบของฉัน โปรดดู
Sergiy Kolodyazhnyy

1
ขอบคุณที่ทำให้ฉันเข้าใจว่าทำไมเครื่องหมายอัฒภาคจึงต้องอยู่ที่นั่น นอกจากนี้คำอธิบายที่ดีของการอ้างอิง
โจ

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