ค้นหา -exec ที่มีหลายคำสั่ง


429

ฉันกำลังพยายามใช้ find -exec ที่มีหลายคำสั่งโดยไม่ประสบความสำเร็จ ใครบ้างรู้ว่าคำสั่งดังต่อไปนี้เป็นไปได้?

find *.txt -exec echo "$(tail -1 '{}'),$(ls '{}')" \;

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



1
เท่าที่ตรวจสอบความเป็นไปได้ของคำสั่งคุณไม่ลองใช้กับระบบของคุณหรือไม่?
ศรีราม

1
จากfindหน้าคู่มือ: There are unavoidable security problems surrounding use of the -exec option; you should use the -execdir option instead.unixhelp.ed.ac.uk/CGI/man-cgi?find
JVE999

1
ที่เกี่ยวข้อง: unix.stackexchange.com/questions/156008/…
Kusalananda

@ ลิงค์ JVE999 เสียหรือไม่ก็ทางเลือกที่ss64.com/bash/find.html
Keith M

คำตอบ:


658

findยอมรับ-execคำสั่งหลายส่วน ตัวอย่างเช่น:

find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;

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

find . -name "*.txt" \( -exec echo {} \; -o -exec true \; \) -exec grep banana {} \;

grep สองครั้งอย่างไร สิ่งนี้ล้มเหลว: find ./* -exec grep -v 'COLD,' {} \; -exec egrep -i "my_string" {} \;
rajeev

51
@rajeev exec ที่สองจะรันต่อเมื่อโค้ดส่งคืนสำหรับการส่งคืนแรกสำเร็จมิฉะนั้นจะถูกข้าม นี่อาจจะถูกบันทึกไว้ในคำตอบนี้
แม็กเคเล็บ

1
นี่จะเป็นคำตอบ
pylover

1
สังเกตการใช้-nคำตอบอื่น ๆ ในการระงับการขึ้นบรรทัดใหม่ที่สร้างโดย echo ซึ่งจะเป็นประโยชน์ถ้าคำสั่งที่สองของคุณสร้างเอาต์พุตเพียงบรรทัดเดียวและคุณต้องการให้ง่ายต่อการอ่าน
William Turrell

ทางออกที่ดี ทำงานเหมือนจับใจ
Philippe Delteil

98
find . -type d -exec sh -c "echo -n {}; echo -n ' x '; echo {}" \;

3
หากคุณต้องการเรียกใช้ Bash แทน Bourne คุณสามารถใช้... -exec bash -c ...แทน... -exec sh -c ...ได้
Kenny Evitt

9
ไม่เคยฝัง{}ในรหัสเปลือก ดูunix.stackexchange.com/questions/156008/…
Kusalananda

3
+1 @Kusalananda ชื่อไฟล์การฉีดนั้นบอบบางและไม่ปลอดภัย ใช้พารามิเตอร์ ดูSC2156
pambda

52

หนึ่งในสิ่งต่อไปนี้:

find *.txt -exec awk 'END {print $0 "," FILENAME}' {} \;

find *.txt -exec sh -c 'echo "$(tail -n 1 "$1"),$1"' _ {} \;

find *.txt -exec sh -c 'echo "$(sed -n "\$p" "$1"),$1"' _ {} \;

12
ขีดล่างก่อน {} มีไว้เพื่ออะไร
qed

2
@qed: $0มันเป็นค่าที่โยนไปที่เก็บสถานที่ของ ลองทำด้วย "foobar" แทน "_": find /usr/bin -name find -exec sh -c 'echo "[$0] [$1]"' foobar {} \;- ผลลัพธ์: "[foobar] [/ usr / bin / find]"
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

1
@XuWang: ใช่ฉันจะบอกว่าเป็นกรณี อย่างที่คุณทราบ$0มักเป็นชื่อโปรแกรม ( ARGV[0])
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

3
เป็นสิ่งสำคัญสำหรับวิธีการนี้สคริปต์ที่ส่งผ่านไปยังsh -cอยู่ในเครื่องหมายคำพูดเดียวไม่ใช่สองเท่า มิฉะนั้น$1จะอยู่ในขอบเขตที่ไม่ถูกต้อง
Nick

1
@ เครื่องหมายคำพูดไม่มีอะไรเกี่ยวข้องกับมัน - คุณสามารถเขียน'$1'ด้วยเครื่องหมายคำพูดคู่ได้ตราบใดที่คุณยังคงหลีกเลี่ยงตัวแปร ( "\$1") คุณสามารถหลบหนีตัวละครอื่น ๆ ได้เช่นกัน ( "\"")
Camilo Martin

22

อีกวิธีหนึ่งเป็นเช่นนี้:

multiple_cmd() { 
    tail -n1 $1; 
    ls $1 
}; 
export -f multiple_cmd; 
find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;

ในหนึ่งบรรทัด

multiple_cmd() { tail -1 $1; ls $1 }; export -f multiple_cmd; find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
  • " multiple_cmd()" - เป็นฟังก์ชั่น
  • " export -f multiple_cmd" - จะส่งออกเพื่อให้ subshell อื่น ๆ สามารถดูได้
  • " find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;" - ค้นหาที่จะใช้งานฟังก์ชันในตัวอย่างของคุณ

ด้วยวิธีนี้ multiple_cmd อาจยาวและซับซ้อนเท่าที่คุณต้องการ

หวังว่านี่จะช่วยได้


สมบูรณ์แบบสิ่งที่ฉันต้องการ!
Anentropic

ไม่ทำงานสำหรับฉันใน OSX
Thomas

@ โทมัสมัน แต่ลองซับนี้ทดสอบใน osx ฉันทำไดเรกทอรีชื่อ 'aaa' โดยมีไฟล์ / dirs อยู่ในนั้นและ CDd จากนั้น ~/aaa$ acmd() { echo x \"$1\" x; }; export -f acmd; find . -exec bash -c 'acmd {}' \;
barlop

@barlop ฉันจะลองดูสิ ขอบคุณ!
โทมัส

14

มีวิธีที่ง่ายกว่า:

find ... | while read -r file; do
    echo "look at my $file, my $file is amazing";
done

อีกวิธีหนึ่งคือ:

while read -r file; do
    echo "look at my $file, my $file is amazing";
done <<< "$(find ...)"

1
ชื่อไฟล์สามารถมีการขึ้นบรรทัดใหม่ในพวกเขานี่คือเหตุผลที่ find มีอาร์กิวเมนต์
-print0

@abasterfield ฉันมักจะหวังว่าจะไม่พบคนที่อยู่ในป่า lol
Camilo Martin

1
สิ่งที่ฉันต้องการจะทำคือ "ค้นหา ... -exec zcat {} | wc -l \;" ซึ่งไม่ได้ผล อย่างไรก็ตามค้นหา ... | ในขณะที่อ่านไฟล์ -r; ทำเสียงสะท้อน "$ file: zcat $file | wc -l"; ทำสำเร็จแล้วขอบคุณมาก!
Greg Dougherty

ในความคิดเห็นข้างต้นฉันมี "ย้อนกลับเห็บ" รอบ "zcat $ file | wc -l" น่าเสียดายที่ SO เปลี่ยนสิ่งเหล่านั้นให้เป็นรูปแบบดังนั้นฉันจึงได้เพิ่มเป็นคำตอบที่แท้จริงพร้อมรหัสที่ถูกต้อง
Greg Dougherty

1
@GregDougherty คุณสามารถหนี backticks `เพื่อทำเช่นนั้นโดยใช้แบ็กสแลชดังนี้: \​`(ยังเป็นอีกเหตุผลที่ดีที่จะใช้$()แทน)
Camilo Martin

8

ฉันไม่รู้ว่าคุณสามารถทำสิ่งนี้กับ find ได้หรือไม่ แต่โซลูชันทางเลือกคือการสร้างเชลล์สคริปต์และเรียกใช้ด้วย find

lastline.sh:

echo $(tail -1 $1),$1

ทำให้สคริปต์เรียกใช้งานได้

chmod +x lastline.sh

การใช้find:

find . -name "*.txt" -exec ./lastline.sh {} \;

7
backticks เลิกใช้แล้วโปรดส่งเสริมการใช้ $ (... ) ซึ่งอ่านได้ง่ายขึ้นดีกว่าใช้อักษรและง่ายต่อการซ้อน ขอบคุณ.
ผู้ใช้ที่ไม่รู้จัก

4

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

เก็บไฟล์. log ล่าสุด 10,000 บรรทัดซึ่งได้รับการแก้ไขใน 7 วันล่าสุดโดยใช้คำสั่ง 1 exec โดยใช้การอ้างอิง Severals {}

1) ดูว่าคำสั่งจะทำอะไรกับไฟล์:

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "echo tail -10000 {} \> fictmp; echo cat fictmp \> {} " \;

2) ทำ: (ไม่ต้องการ "\>" แต่ต้องการเพียง ">" เท่านั้น)

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "tail -10000 {} > fictmp; cat fictmp > {} ; rm fictmp" \;


แต่นี่จะแตกถ้าชื่อไฟล์หนึ่งมีพื้นที่ฉันเชื่อ
Camilo Martin

4

ขอบคุณ Camilo Martin ฉันสามารถตอบคำถามที่เกี่ยวข้องได้:

สิ่งที่ฉันอยากทำคือ

find ... -exec zcat {} | wc -l \;

ซึ่งไม่ได้ผล อย่างไรก็ตาม

find ... | while read -r file; do echo "$file: `zcat $file | wc -l`"; done

ทำงานได้ดีขอบคุณ!


2

การขยายคำตอบ @ Tinker

ในกรณีของฉันฉันต้องทำcommand | command | commandภายใน-execเพื่อพิมพ์ทั้งชื่อไฟล์และข้อความที่พบในไฟล์ที่มีข้อความบางอย่าง

ฉันสามารถทำได้ด้วย:

find . -name config -type f \( -exec  grep "bitbucket" {} \; -a -exec echo {} \;  \) 

ผลลัพธ์คือ:

    url = git@bitbucket.org:a/a.git
./a/.git/config
    url = git@bitbucket.org:b/b.git
./b/.git/config
    url = git@bitbucket.org:c/c.git
./c/.git/config

1
คุณสามารถพิมพ์ชื่อไฟล์และเนื้อหา grep ในบรรทัดเดียวโดยส่งผ่าน/dev/nullอาร์กิวเมนต์ที่สองไปยังgrepคำสั่งด้วย-execพารามิเตอร์เดียว:find . -name config -type f -exec grep "bitbucket" {} /dev/null \;
Bill Feth

1

ควรใช้ xargs :)

find *.txt -type f -exec tail -1 {} \; | xargs -ICONSTANT echo $(pwd),CONSTANT

อีกอันหนึ่ง (ทำงานกับ osx)

find *.txt -type f -exec echo ,$(PWD) {} + -exec tail -1 {} + | tr ' ' '/'

3
สิ่งนี้มองเห็นกรณีการใช้งานที่สำคัญสำหรับfind- สถานการณ์ที่จำนวนไฟล์ที่ตรงกันใหญ่เกินไปสำหรับบรรทัดคำสั่ง -execเป็นวิธีที่จะได้รับรอบขีด จำกัด นี้ การใช้ประโยชน์จากยูทิลิตี้นั้นพลาดไป
Chris Johnson

xargs -nมีอยู่เพื่อเลือกจำนวนการแข่งขันต่อการเรียกใช้ xargs -n 1 foocmdจะดำเนินการfoocmd {}สำหรับทุกการแข่งขัน
AndrewF

0

find+xargsคำตอบ

ตัวอย่างด้านล่างค้นหา.htmlไฟล์ทั้งหมดและสร้างสำเนาโดยมี.BAKนามสกุลต่อท้าย (เช่น1.html> 1.html.BAK)

คำสั่งเดียวที่มีตัวยึดหลายตำแหน่ง

find . -iname "*.html" -print0 | xargs -0 -I {} cp -- "{}" "{}.BAK"

หลายคำสั่งที่มีตัวยึดหลายตำแหน่ง

find . -iname "*.html" -print0 | xargs -0 -I {} echo "cp -- {} {}.BAK ; echo {} >> /tmp/log.txt" | sh

# if you need to do anything bash-specific then pipe to bash instead of sh

คำสั่งนี้จะทำงานกับไฟล์ที่ขึ้นต้นด้วยยัติภังค์หรือมีช่องว่างเช่น-my file.htmlด้วยการอ้างอิงพารามิเตอร์และ--หลังจากนั้นcpสัญญาณไปcpยังจุดสิ้นสุดของพารามิเตอร์และจุดเริ่มต้นของชื่อไฟล์จริง

-print0 ไพพ์ผลลัพธ์ด้วยเทอร์มินัล null-byte


สำหรับxargs-I {}พารามิเตอร์กำหนด{}เป็นตัวยึด; คุณสามารถใช้ตัวยึดตำแหน่งใดก็ได้ที่คุณต้องการ -0ระบุว่ารายการอินพุตนั้นคั่นด้วย null

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