วิธีหยุดคำสั่ง find หลังจากการจับคู่ครั้งแรก


131

มีวิธีบังคับให้findคำสั่งหยุดทันทีหลังจากค้นหาคู่แรกหรือไม่


4
The Unseen: สาเหตุที่ออกหลังจากห้าผลลัพธ์เมื่อ piped ไปที่ head -n 5 เป็นเพราะ head ออกหลังจากผลลัพธ์ห้ารายการ เมื่อหัวหน้าออกจากท่อปิดและส่งสัญญาณไปยังโปรแกรมไปป์ไลน์เพื่อยุติเช่นกัน ขออภัยที่ไม่ตอบกลับคุณโดยตรงคุณต้องมี 50 ชื่อเสียงในการตอบกลับ
Ruste

คำตอบ:


148

ด้วย GNU หรือ FreeBSD findคุณสามารถใช้เพรดิเคต-quit:

find . ... -print -quit

NetBSD findเทียบเท่า:

find . ... -print -exit

หากสิ่งที่คุณทำคือการพิมพ์ชื่อและสมมติว่าชื่อไฟล์ไม่มีอักขระขึ้นบรรทัดใหม่คุณสามารถทำได้:

find . ... -print | head -n 1

ที่จะไม่หยุดfindหลังจากการแข่งขันครั้งแรก แต่อาจขึ้นอยู่กับเวลาและบัฟเฟอร์ในการแข่งขันครั้งที่สองหรือ (มาก) ในภายหลัง โดยทั่วไปfindจะถูกยกเลิกด้วย SIGPIPE เมื่อพยายามส่งออกบางสิ่งในขณะที่headหายไปเพราะอ่านแล้วและแสดงบรรทัดแรกของอินพุต

โปรดทราบว่าเปลือกบางส่วนจะไม่รอfindคำสั่งนั้นหลังจากheadส่งคืนแล้ว บอร์นเชลล์และการใช้งาน AT&T ของksh(เมื่อไม่ใช่แบบอินเทอร์แอคทีฟ) และyash(เฉพาะในกรณีที่ไพพ์ไลน์นั้นเป็นคำสั่งสุดท้ายในสคริปต์) จะไม่ปล่อยให้มันทำงานในพื้นหลัง หากคุณต้องการเห็นพฤติกรรมดังกล่าวในเชลล์ใด ๆ คุณสามารถเปลี่ยนข้างต้นเป็น:

(find . ... -print &) | head -n 1

หากคุณกำลังทำมากกว่าการพิมพ์พา ธ ของไฟล์ที่พบคุณสามารถลองวิธีนี้:

find . ... -exec sh -c 'printf "%s\n" "$1"; kill "$PPID"' sh {} \;

(แทนที่printfด้วยสิ่งที่คุณจะทำกับไฟล์นั้น)

นั่นคือผลข้างเคียงของการfindคืนสถานะทางออกซึ่งสะท้อนถึงความจริงที่ว่ามันถูกฆ่า

ที่จริงแล้วการใช้สัญญาณ SIGPIPE แทนที่จะเป็น SIGTERM ( kill -s PIPEแทนที่จะเป็นkill) จะทำให้เปลือกหอยบางตัวเงียบกว่าเกี่ยวกับความตายนั้น (แต่จะยังคงสถานะการออกที่ไม่ใช่ศูนย์)


3
ในกรณีที่ทุกคนต้องการทดสอบว่าไฟล์ใดตรงกับเพรดิเคตหรือไม่ให้หยุดทันทีที่พบไฟล์หนึ่งใน Bash และ GNU Find คุณสามารถทำได้: if [[ $(find ... -print -quit) ]]; then ...เพียงแค่ทดสอบว่าค้นหาสิ่งที่พิมพ์ออกมาหรือไม่
Tobia

@Tobia ควรใส่$(…)ส่วนของเครื่องหมายอัญประกาศในกรณีที่คุณใช้เพียงวงเล็บเดียว ( [ … ])
phk

@phk ยกเว้นว่าฉันไม่ได้ใช้วงเล็บเดี่ยว (เพราะมันแย่มาก) ดังนั้นฉันไม่จำเป็นต้องใช้เครื่องหมายคำพูด
Tobia

2
@Tobia [เป็นคำสั่งมาตรฐาน มันไม่มากคำสั่งที่น่ากลัว แต่วิธีเชลล์เหมือน Bourne แยกบรรทัดคำสั่ง [[...]]เป็นโครงสร้าง ksh ที่มีปัญหาของตัวเองในเปลือกหอยต่างๆ ตัวอย่างเช่นจนกระทั่งเมื่อเร็ว ๆ นี้[[ $(...) ]]จะไม่ทำงานzsh(คุณต้องการ[[ -n $(...) ]]) ยกเว้นในzshคุณต้องมีเครื่องหมายอัญประกาศคู่[[ $a = $b ]]ซึ่ง[[ =~ ]]มีความแตกต่างที่เข้ากันไม่ได้ระหว่างการนำไปใช้งานและระหว่างเวอร์ชันสำหรับ bash และข้อบกพร่องหลายรายการในบางรายการ [ส่วนตัวผมชอบ
Stéphane Chazelas

คือ...อะไร .
kyb

11
find . -name something -print -quit

ยุติการค้นหาหลังจากการจับคู่ครั้งแรกหลังจากพิมพ์

ยุติการค้นหาหลังจากจำนวนการจับคู่และผลการพิมพ์ที่ระบุ:

find . -name something -print | head -n 5

น่าประหลาดใจมากพอ - ตอนนี้หัวหน้าจบสายหลังจาก 5 แมตช์ แต่ฉันไม่รู้ว่าทำไมหรืออย่างไร

มันง่ายมากที่จะทดสอบ เพียงแค่ให้ค้นหาค้นหาบนรากซึ่งจะส่งผลพันแมตช์อาจจะมากยิ่งขึ้นในขณะที่การไม่น้อยกว่าหนึ่งนาทีหรือมากกว่า แต่เมื่อ piped ไปที่ "head" "find" จะสิ้นสุดหลังจากจำนวนบรรทัดที่ระบุไว้ใน head (head เริ่มต้นแสดง 10 ให้ใช้ "head -n" เพื่อระบุ lines)

โปรดทราบว่าการดำเนินการนี้จะยุติหลังจาก "head -n" ถึงจำนวนอักขระบรรทัดใหม่ที่ระบุดังนั้นการจับคู่ใด ๆ ที่มีอักขระขึ้นบรรทัดใหม่หลายรายการจะนับตามนั้น


ฉันได้สังเกตด้วยว่า 'โปรแกรมนี้สิ้นสุดลงหลังจากที่หัวอ่านเสร็จสิ้นด้วยปรากฏการณ์เอาต์พุต' แต่ก็ไม่ได้ข้ามเชลล์อย่างสม่ำเสมอ ผมคิดว่าบุญนี้คำถามของตัวเอง - โชคดีสำหรับทุบตีคำตอบอยู่แล้วที่ StackOverflow ของทุบตี: หัวและหางพฤติกรรมกับสคริปต์ทุบตี ที่ให้ข้อมูลเพียงพอสำหรับฉันที่จะสรุปได้ว่าโปรแกรมจะยุติหรือดำเนินการต่อในพื้นหลังหรือไม่นั้นขึ้นอยู่กับการตอบสนองของ SIGPIPE การฆ่าเป็นค่าเริ่มต้น
ปราชญ์

ฉันหมายถึง 'ข้าม * โปรแกรม * / shells' แต่เห็นได้ชัดว่า unix.stackexchange.com ต้องการให้ฉันบันทึกสิ่งนี้เป็นความคิดเห็นที่สองมากกว่าให้ฉันแก้ไขความคิดเห็นแรกของฉัน นอกจากนี้ฉันเห็นแล้วว่า @Ruste แสดงความคิดเห็นกับผลกระทบนี้ที่ด้านบนซึ่งไม่ได้ช่วยฉันตั้งแต่แรกเลยตั้งแต่ฉันตอบคำตอบ ...
ปราชญ์

2

เพื่อความบันเทิงนี่คือตัวสร้างการค้นหาที่ขี้เกียจใน Bash ตัวอย่างนี้สร้างเสียงเรียกเข้าไฟล์ในไดเรกทอรีปัจจุบัน อ่านหลาย ๆ เรื่องที่คุณต้องการkill %+(อาจจะแค่ 1)

#!/usr/bin/env bash
unset -v files n
trap 'kill "$x_PID"' EXIT

coproc x while :; do
    find . -type f -maxdepth 1 -exec sh -c "$(</dev/fd/3)" _ {} +
done 4<&0 <<\EOF 3<&0 <&4-
for x; do
    read -r _
    printf '%s\0' "$x"
done
EOF

while
    echo >&${x[1]}
    IFS= read -rd '' -u "$x" 'files[n++]'
do
    printf '%q ' "${files[@]}"
    echo
    sleep .2
done

1

grep จะส่งคืนถ้าใช้กับแฟล็-mกด้วยเช่นกัน

find stuff | grep -m1 .

มันจะกลับมาหลังจากบรรทัดแรกที่พิมพ์โดยค้นหา

ความแตกต่างระหว่างสิ่งนี้และfind stuff -print -quit | head -1คือถ้าการค้นหาเร็ว grep มากพออาจไม่สามารถหยุดกระบวนการได้ทันเวลา (ไม่สำคัญเลยจริงๆ) ในขณะที่ถ้าการค้นหายาวเกินไปก็จะพบว่าพิมพ์ไม่จำเป็นมาก เส้น

สิ่งนี้จะใช้งานได้กับ busybox find แม้ว่าจะเป็น grep busybox -mก็ไม่จำเป็นต้องใช้เช่นกัน

find /tmp/stuff -exec "sh" "-c" "eval 'echo {}; { kill \$PPID; }'" \;

สิ่งนี้จะคายข้อความเกี่ยวกับกระบวนการค้นหาที่ได้รับสัญญาณ sigterm (ปกติ) แต่เอาต์พุตนี้เป็นของเชลล์ที่กำลังรันไม่ใช่คำสั่ง find ดังนั้นจึงไม่ยุ่งกับเอาต์พุตคำสั่งซึ่งหมายความว่าไพพ์หรือการเปลี่ยนเส้นทางจะออกเพียงบรรทัด จับคู่ด้วยการค้นหา

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