bash - ฉันสามารถทำได้: ค้นหา ... - ทำสิ่งนี้ && ได้ไหม?


23

มีวิธีการรวมคำสั่งเชลล์สองคำสั่งที่เรียกใช้กับfind - execหรือไม่?

เช่นการพิมพ์ไฟล์. csvทั้งหมดที่มีสตริง foo พร้อมกับการเกิดขึ้นของมันฉันต้องการจะทำ:

find . -iname \*.csv -exec grep foo {} && echo {} \;

แต่ทุบตีบ่นกับ "อาร์กิวเมนต์ที่หายไปเป็น '-exec'"


2
คุณสามารถใช้ 2 ในลำดับหรือใช้เพียงครั้งเดียว-exec -exec sh -c 'grep foo "$0" && printf %s\\n "$0"' {} \;
jw013

สิ่งนี้ทำให้ฉันสะดุดซ้ำแล้วซ้ำอีก: ฉันมักจะคาดหวังว่าอาร์กิวเมนต์แรกที่ส่งผ่านไปยังsh(ในกรณีนี้{}) จะเป็น$1และ$0จะเป็นเช่นshนั้น $0แต่ในความเป็นจริงคุณถูกต้องแสดงให้เห็นว่าอาร์กิวเมนต์แรกขึ้นเป็น การมีอาร์กิวเมนต์แรกเป็นชื่อของคำสั่งที่เรียกใช้เป็นเพียงการประชุมที่ไม่ได้บังคับใช้โดยอัตโนมัติในกรณีเหล่านี้
dubiousjim

อาจจะถูกรวมเข้ากับคำถามนี้: unix.stackexchange.com/q/18077/4801
dubiousjim

คำตอบ:


24

-execเป็นเพรดิเคตที่รันคำสั่ง (ไม่ใช่เชลล์) และประเมินผลเป็นจริงหรือเท็จตามผลลัพธ์ของคำสั่ง (สถานะออกเป็นศูนย์หรือไม่เป็นศูนย์)

ดังนั้น:

find . -iname '*.csv' -exec grep foo {} \; -print

จะพิมพ์เส้นทางของไฟล์หากgrepพบว่า foo ในไฟล์ แทนคุณ-printสามารถใช้ภาค-execแสดงอื่นหรือภาคอื่น ๆ

find . -iname '*.csv' -exec grep foo {} \; -exec echo {} \;

ดูเพิ่มเติม!และ-oพบว่าผู้ประกอบการสำหรับการปฏิเสธและหรือ

หรือคุณสามารถเริ่มเชลล์เป็น:

find . -iname '*.csv' -exec sh -c '
   grep foo "$1" && echo "$1"' sh {} \;

หรือเพื่อหลีกเลี่ยงการเริ่มเชลล์สำหรับทุกไฟล์:

find . -iname '*.csv' -exec sh -c '
  for i do
    grep foo "$i" && echo "$i"
  done' sh {} +

10

ปัญหาที่คุณกำลังหันหน้าไปก็คือว่าเปลือกแรกแยกวิเคราะห์บรรทัดคำสั่งและเห็นทั้งสองคำสั่งง่ายๆแยกจากกันโดย&&ผู้ประกอบการ: และfind . -iname \*.csv -exec grep foo {} echo {} \;เธซเธฑ&&( find . -iname \*.csv -exec grep foo {} '&&' echo {} \;) ทะลุว่า แต่ตอนนี้คำสั่งดำเนินการโดยfindเป็นสิ่งที่ต้องการgrepด้วยข้อโต้แย้งfoo, wibble.csv, &&, และecho wibble.csvคุณต้องสั่งfindให้เรียกใช้เชลล์ที่จะตีความ&&โอเปอเรเตอร์:

find . -iname \*.csv -exec sh -c 'grep foo "$0" && echo "$0"' {} \;

หมายเหตุว่าอาร์กิวเมนต์แรกหลังจากที่sh -c SOMECOMMANDเป็นไม่ได้$0$1

-exec … +คุณสามารถบันทึกเวลาเริ่มต้นของกระบวนการเปลือกสำหรับทุกไฟล์โดยการจัดกลุ่มสวดคำสั่งด้วย เพื่อความสะดวกในการประมวลผลให้ส่งค่าดัมมี่บางส่วน$0เพื่อ"$@"ระบุชื่อไฟล์

find . -iname \*.csv -exec sh -c 'for x in "$@"; do grep foo "$x" && echo "$x"; done' \ {} +

หากคำสั่งเชลล์เป็นเพียงสองโปรแกรมคั่นด้วย&&, findสามารถทำงานด้วยตัวเอง: การเขียนเป็นลำดับสอง-execกระทำและคนที่สองจะได้รับการดำเนินการอย่างใดอย่างหนึ่งถ้าออกครั้งแรกกับสถานะ 0

find . -iname \*.csv -exec grep foo {} \; -exec echo {} \;

(ผมคิดว่าgrepและechoเป็นเพียงเพื่อวัตถุประสงค์ภาพประกอบเป็น-exec echoจะถูกแทนที่ด้วย-printและผลผลิตที่เกิดขึ้นไม่ได้โดยเฉพาะอย่างยิ่งอยู่แล้วที่มีประโยชน์.)


2
ฉันมักจะหลีกเลี่ยงการใช้ "$ 0" สำหรับสิ่งนี้เนื่องจากเชลล์ใช้เพื่อแสดงข้อความแสดงข้อผิดพลาด ตัวอย่างเช่นคุณอาจเห็น./some-file: grep: command not foundข้อความแสดงข้อผิดพลาดที่ทำให้เกิดความสับสน -exec find sh -c '... "$1"' sh {} \;จะไม่มีปัญหา มีคำที่พิมพ์ผิด (ที่เกี่ยวข้อง) ในคำสั่ง find ที่สองของคุณ
Stéphane Chazelas

3

ในกรณีเฉพาะนี้ฉันจะทำ:

find . -iname \*.csv -exec grep -l foo \{\} \;

หรือถ้าคุณมีack :

ack -al -G '.*\.csv' foo

ในการตอบคำถามจริงของคุณสิ่งนี้อาจใช้ได้:

find . -iname \*.csv -exec sh -c "grep foo {} && echo {}" \;


3
คำสั่ง find หลังนั้นไม่เพียง แต่ไม่พกพาเท่านั้น แต่ยังมีอันตรายมากเนื่องจากพา ธ ของไฟล์ถูกประเมินว่าเป็นรหัสเชลล์
Stéphane Chazelas

ทั้งหมดเป็นเหตุผลที่มากขึ้นและพยายามหลีกเลี่ยงได้โดยใช้แอ๊ / grep ถูกต้อง :)
เดนนิส Kaarsemaker

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