ฉันจะใช้คำสั่ง bash สองคำสั่งใน -exec ของคำสั่ง find ได้อย่างไร


32

เป็นไปได้หรือไม่ที่จะใช้ 2 คำสั่งใน-execส่วนของfindคำสั่ง?

ฉันลองทำสิ่งที่ชอบแล้ว:

find . -name "*" -exec  chgrp -v new_group {}  ; chmod -v 770 {}  \;

และฉันได้รับ:

ค้นหา: อาร์กิวเมนต์ที่หายไปไปยัง -exec
chmod: ไม่สามารถเข้าถึง {}: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว
chmod: ไม่สามารถเข้าถึงได้: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว

คำตอบ:


44

สำหรับfindคำสั่งคุณสามารถเพิ่ม-execคำสั่งเพิ่มเติมในแถวได้ด้วย:

find . -name "*" -exec chgrp -v new_group '{}' \; -exec chmod -v 770 '{}' \;

โปรดทราบว่าในคำสั่งนี้เทียบเท่ากับการใช้

chgrp -v ไฟล์ new_group && chmod -v 770 ไฟล์

ในแต่ละไฟล์

ทั้งหมดfindของพารามิเตอร์เช่น-name, -exec, -sizeและอื่น ๆ ที่เป็นจริงการทดสอบ : findจะทำงานต่อไปพวกเขาหนึ่งโดยหนึ่งตราบเท่าที่ห่วงโซ่ทั้งหมดเพื่อให้ห่างไกลได้มีการประเมินไปจริง ดังนั้นแต่ละติดต่อกัน-execคำสั่งจะถูกดำเนินการเฉพาะในกรณีที่คนก่อนหน้านี้กลับมาจริง (เช่น0ออกจากสถานะของคำสั่ง) แต่findยังเข้าใจผู้ประกอบการเชิงตรรกะเช่นหรือ ( -o) และไม่ ( !) ดังนั้นในการใช้การ-execทดสอบหลาย ๆห่วงโซ่โดยไม่คำนึงถึงผลลัพธ์ก่อนหน้าสิ่งหนึ่งจะต้องใช้สิ่งนี้:

find . -name "*" \( -exec chgrp -v new_group {} \; -o -exec chmod -v 770 {} \; \)

4
+1: ใช่นั่นเป็นวิธีที่ยอดเยี่ยมที่สุดที่จะทำ หากคุณสามารถอธิบายได้ว่าเหตุใดคุณจึงใช้'{}'(เครื่องหมายอัญประกาศรอบเครื่องหมายวงเล็บ) โปรดไปที่: unix.stackexchange.com/q/8647/4485
ผู้ใช้ไม่ทราบ

1
@user น่าเสียดายที่ฉันไม่รู้ว่ายังจำเป็นหรือไม่ ตอนนี้ฉันทำการทดสอบแล้วและยังไม่เจอสถานการณ์ที่จะเปลี่ยนแปลงอะไร ฉันคิดว่ามันเป็นแค่ "แนวปฏิบัติที่ดี" ที่จะตายไป
rozcietrzewiacz

2
เครื่องหมายคำพูดมีความสำคัญสำหรับไฟล์ที่มีช่องว่างในชื่อ
naught101

17
find . -name "*" -exec sh -c 'chgrp -v new_group "$0" ; chmod -v 770 "$0"' {} \;

@Gilles: ความมหัศจรรย์ของ -cจัดการที่แปลกประหลาดของ $ 0 ทำให้ฉันคิดว่านี่เป็นสิ่งที่ผิดทุกครั้งที่ฉันมองมัน แต่มันถูกต้องแน่นอน
Derobert

ฉันเหมือนเปลือกอย่างชัดเจนถูกกำหนด ...
djangofan

คำตอบนี้ (และคำตอบของไจล์ส) sh -cดูเหมือนว่าคำตอบที่ดีสำหรับคำถามที่ได้รับ

14

คำสั่งของคุณถูกแยกวิเคราะห์โดยเชลล์เป็นสองคำสั่งโดยคั่นด้วย a ;ซึ่งเทียบเท่ากับการขึ้นบรรทัดใหม่:

find . -name "*" -exec chgrp -v new_group {}
chmod -v 770 {} \;

หากคุณต้องการเรียกใช้คำสั่งเชลล์ให้เรียกใช้เชลล์อย่างชัดเจนด้วยbash -c(หรือsh -cหากคุณไม่สนใจว่าเชลล์นั้นมีการทุบตีโดยเฉพาะ):

find . -name "*" -exec sh -c 'chgrp -v new_group "$0"; chmod -v 770 "$0"' {} \;

หมายเหตุการใช้{}เป็นอาร์กิวเมนต์ของเชลล์ มันเป็นข้อโต้แย้งข้อที่ศูนย์ (ซึ่งเป็นปกติชื่อของเปลือกหรือสคริปต์ แต่นี่ไม่ได้เรื่องที่นี่) "$0"อ้างอิงจึงเป็น

คุณสามารถส่งชื่อไฟล์หลายชื่อให้กับเชลล์ในแต่ละครั้งและทำให้เชลล์ทำซ้ำผ่านมันจะเร็วขึ้น ที่นี่ฉันผ่าน_เป็นชื่อสคริปต์และอาร์กิวเมนต์ต่อไปนี้เป็นชื่อไฟล์ซึ่งfor x(ทางลัดสำหรับfor x in "$@") iterates มากกว่า

find . -name "*" -exec sh -c 'for x; do chgrp -v new_group "$x"; chmod -v 770 "$x"; done' _ {} +

โปรดทราบว่าตั้งแต่ bash 4 หรือเป็น zsh คุณไม่จำเป็นต้องค้นหาเลย ในทุบตีเรียกใช้shopt -s globstar(ใส่ไว้ในของคุณ~/.bashrc) เพื่อเปิดใช้งานการ**/ยืนสำหรับไดเรกทอรีเรียกซ้ำ (ใน zsh จะเปิดใช้งานตลอดเวลา) จากนั้น

chgrp -v new_group -- **/*; chmod -v 770 -- **/*

หรือหากคุณต้องการให้มีการวนซ้ำไฟล์ตามลำดับ

for x in **/*; do
  chgrp -v new_group -- "$x"
  chmod -v 770 -- "$x"
done

ความแตกต่างอย่างหนึ่งของfindคำสั่งคือเชลล์จะละเว้นไฟล์ dot (ไฟล์ที่ชื่อขึ้นต้นด้วย a .) หากต้องการรวมไว้ในทุบตีชุดแรกGLOBIGNORE=.:.. ; ใน zsh ใช้**/*(D)เป็นรูปแบบ glob


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