ใช้คำสั่ง find แต่ไม่รวมไฟล์ในสองไดเร็กทอรี


88

ฉันต้องการค้นหาไฟล์ที่ลงท้ายด้วย_peaks.bedแต่ไม่รวมไฟล์ในtmpและscriptsโฟลเดอร์

คำสั่งของฉันเป็นดังนี้:

 find . -type f \( -name "*_peaks.bed" ! -name "*tmp*" ! -name "*scripts*" \)

แต่มันไม่ได้ผล ไฟล์ในtmpและscriptโฟลเดอร์จะยังคงแสดงอยู่

ใครมีความคิดเกี่ยวกับเรื่องนี้?

คำตอบ:


192

นี่คือวิธีที่คุณสามารถระบุได้ด้วยfind:

find . -type f -name "*_peaks.bed" ! -path "./tmp/*" ! -path "./scripts/*"

คำอธิบาย:

  • find . - เริ่มค้นหาจากไดเร็กทอรีการทำงานปัจจุบัน (เรียกซ้ำตามค่าเริ่มต้น)
  • -type f- ระบุfindว่าคุณต้องการเฉพาะไฟล์ในผลลัพธ์
  • -name "*_peaks.bed" - ค้นหาไฟล์ที่มีชื่อลงท้ายด้วย _peaks.bed
  • ! -path "./tmp/*" - ไม่รวมผลลัพธ์ทั้งหมดที่มีเส้นทางขึ้นต้นด้วย ./tmp/
  • ! -path "./scripts/*" - ไม่รวมผลลัพธ์ทั้งหมดที่มีเส้นทางขึ้นต้นด้วย ./scripts/

การทดสอบโซลูชัน:

$ mkdir a b c d e
$ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
$ find . -type f ! -path "./a/*" ! -path "./b/*"

./d/4
./c/3
./e/a
./e/b
./e/5

คุณค่อนข้างใกล้-nameตัวเลือกจะพิจารณาเฉพาะชื่อฐานโดย-pathพิจารณาเส้นทางทั้งหมด =)


ทำได้ดีมาก _peaks.bedแต่คุณลืมหนึ่งในสิ่งที่สหกรณ์ต้องการที่จะหาไฟล์ที่ลงท้ายด้วย
alex

2
สิ่งนี้ใช้ส่วนขยายจำนวนมากใน GNU findแต่เนื่องจากคำถามถูกแท็ก Linux จึงไม่เป็นปัญหา คำตอบที่ดี.
Jonathan Leffler

1
หมายเหตุสั้น ๆ : หากคุณใช้.ในข้อความค้นหาเริ่มต้นคุณต้องใช้ในแต่ละเส้นทางที่คุณยกเว้น การจับคู่เส้นทางค่อนข้างเข้มงวดไม่ได้ทำการค้นหาที่คลุมเครือ ดังนั้นหากคุณใช้find / -type f -name *.bed" ! -path "./tmp/"มันจะไม่ได้ผล คุณต้อง! -path "/tmp"ทำให้มีความสุข
ลอกแมน

3
โปรดทราบว่า * มีความสำคัญ $ ! -path "./directory/*"
Thomas Bennett

3
ตามหน้าคน: "ในการละเว้นโครงสร้างไดเรกทอรีทั้งหมดให้ใช้-pruneแทนที่จะตรวจสอบทุกไฟล์ในโครงสร้าง" หากไดเรกทอรีที่ยกเว้นของคุณทำงานลึกมากหรือมีไฟล์จำนวนมากและคุณสนใจเกี่ยวกับประสิทธิภาพให้ใช้-pruneตัวเลือกนี้แทน
thdoan

8

นี่คือวิธีหนึ่งที่คุณสามารถทำได้ ...

find . -type f -name "*_peaks.bed" | egrep -v "^(./tmp/|./scripts/)"

2
นี้มีบุญของการทำงานกับรุ่นใด ๆfindมากกว่าเฉพาะกับ findGNU อย่างไรก็ตามคำถามถูกแท็ก Linux ดังนั้นจึงไม่สำคัญ
Jonathan Leffler

2

ใช้

find \( -path "./tmp" -o -path "./scripts" \) -prune -o  -name "*_peaks.bed" -print

หรือ

find \( -path "./tmp" -o -path "./scripts" \) -prune -false -o  -name "*_peaks.bed"

หรือ

find \( -path "./tmp" -path "./scripts" \) ! -prune -o  -name "*_peaks.bed"

ลำดับเป็นสิ่งสำคัญ จะประเมินจากซ้ายไปขวา เริ่มต้นด้วยการยกเว้นเส้นทางเสมอ

คำอธิบาย

อย่าใช้-not(หรือ!) เพื่อยกเว้นไดเร็กทอรีทั้งหมด ใช้-prune. ตามที่อธิบายไว้ในคู่มือ:

−prune    The primary shall always evaluate as  true;  it
          shall  cause  find  not  to descend the current
          pathname if it is a directory.  If  the  −depth
          primary  is specified, the −prune primary shall
          have no effect.

และใน GNU ค้นหาคู่มือ:

-path pattern
              [...]
              To ignore  a  whole
              directory  tree,  use  -prune rather than checking
              every file in the tree.

อันที่จริงถ้าคุณใช้ค้นหาจะประเมินการแสดงออกสำหรับแต่ละโหนดภายใต้-not -path "./pathname""./pathname"

หานิพจน์เป็นเพียงการประเมินเงื่อนไข

  • \( \)- การดำเนินการแบบกลุ่ม (คุณสามารถใช้ได้-path "./tmp" -prune -o -path "./scripts" -prune -oแต่จะละเอียดกว่า)
  • -path "./script" -prune- ถ้า-pathผลตอบแทนที่เป็นความจริงและเป็นไดเรกทอรีกลับมาจริงสำหรับไดเรกทอรีที่และไม่ได้ลงไปในมัน
  • -path "./script" ! -prune- ประเมินเป็น(-path "./script") AND (! -prune). มันเปลี่ยน "เสมอจริง" ของพรุนเป็นเท็จเสมอ หลีกเลี่ยงการพิมพ์"./script"ที่ตรงกัน
  • -path "./script" -prune -false- ตั้งแต่-pruneเสมอกลับจริงคุณสามารถทำตามมันด้วยจะทำเช่นเดียวกันกว่า-false!
  • -o- หรือตัวดำเนินการ หากไม่มีการระบุตัวดำเนินการระหว่างสองนิพจน์จะมีค่าเริ่มต้นเป็นตัวดำเนินการ AND

ดังนั้นจึง\( -path "./tmp" -o -path "./scripts" \) -prune -o -name "*_peaks.bed" -printขยายเป็น:

[ (-path "./tmp" OR -path "./script") AND -prune ] OR ( -name "*_peaks.bed" AND print )

การพิมพ์มีความสำคัญที่นี่เนื่องจากไม่มีการขยายเป็น:

{ [ (-path "./tmp" OR -path "./script" )  AND -prune ]  OR (-name "*_peaks.bed" ) } AND print

-printถูกเพิ่มโดยการค้นหา - นั่นคือเหตุผลที่ส่วนใหญ่คุณไม่จำเป็นต้องเพิ่มในนิพจน์ของคุณ และตั้งแต่-pruneคืนค่าจริงมันจะพิมพ์ "./script" และ "./tmp"

ไม่จำเป็นสำหรับคนอื่น ๆ เพราะเราเปลี่ยน-pruneไปส่งคืนเท็จเสมอ

คำแนะนำ: คุณสามารถใช้find -D opt expr 2>&1 1>/dev/nullเพื่อดูว่ามีการเพิ่มประสิทธิภาพและขยายอย่างไร
find -D search expr 2>&1 1>/dev/nullเพื่อดูว่ามีการตรวจสอบเส้นทางใด


0

ลองสิ่งที่ชอบ

find . \( -type f -name \*_peaks.bed -print \) -or \( -type d -and \( -name tmp -or -name scripts \) -and -prune \)

และอย่าแปลกใจมากถ้าฉันเข้าใจผิด หากเป้าหมายคือผู้บริหาร (แทนที่จะพิมพ์) ให้แทนที่เป้าหมายนั้นแทน


0

สำหรับฉันวิธีนี้ใช้ไม่ได้กับคำสั่ง exec กับ find ไม่รู้ว่าทำไมทางออกของฉันคือ

find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

คำอธิบาย:เหมือนกับ sampson-chen หนึ่งที่มีการเพิ่มของ

-prune - ละเว้นเส้นทางการดำเนินการของ ...

-o - ถ้าไม่ตรงกันให้พิมพ์ผลลัพธ์ (ตัดไดเร็กทอรีและพิมพ์ผลลัพธ์ที่เหลือ)

18:12 $ mkdir a b c d e
18:13 $ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
18:13 $ find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

gzip: . is a directory -- ignored
gzip: ./a is a directory -- ignored
gzip: ./b is a directory -- ignored
gzip: ./c is a directory -- ignored
./c/3:    0.0% -- replaced with ./c/3.gz
gzip: ./d is a directory -- ignored
./d/4:    0.0% -- replaced with ./d/4.gz
gzip: ./e is a directory -- ignored
./e/5:    0.0% -- replaced with ./e/5.gz
./e/a:    0.0% -- replaced with ./e/a.gz
./e/b:    0.0% -- replaced with ./e/b.gz

คำตอบที่ยอมรับไม่ได้ผล แต่ได้ผล ใช้ลูกพรุน, find . -path ./scripts -prune -name '*_peaks.bed' -type f. ไม่แน่ใจว่าจะแยกหลายไดเรกทอรีได้อย่างไร นอกจากนี้ยังแสดงรายการไดเรกทอรีระดับบนสุดที่ยกเว้นแม้ว่าtypeจะระบุไว้ก็ตาม การยกเว้นผ่าน Grep ดูเหมือนจะตรงไปตรงมามากกว่าเว้นแต่คุณต้องการใช้พรุนเพื่อเร่งการดำเนินการค้นหา
Mohnish

ฉันมีปัญหาในการยกเว้นหลายไดเรกทอรีเช่นกัน แต่ความคิดเห็นด้านบนให้คำตอบว่าได้ผล ฉันใช้ '-not -path' หลายอินสแตนซ์และในแต่ละนิพจน์พา ธ ฉันใส่คำนำหน้าแบบเต็มตามที่ใช้ในพารามิเตอร์แรกเพื่อ 'find' และลงท้ายด้วยเครื่องหมายดอกจัน (และเว้นจุดใด ๆ )
Jetset

0

คุณสามารถลองด้านล่าง:

find ./ ! \( -path ./tmp -prune \) ! \( -path ./scripts -prune \) -type f -name '*_peaks.bed'

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