จะใช้ตัวเลือก '-prune' ของ 'find' in sh ได้อย่างไร?


219

ฉันไม่เข้าใจตัวอย่างที่ได้รับจากman findใครทุกคนสามารถยกตัวอย่างและคำอธิบายให้ฉันได้บ้าง ฉันสามารถรวมการแสดงออกปกติในนั้นได้หรือไม่?


คำถามที่มีรายละเอียดมากขึ้นเป็นเช่นนี้:

เขียนเชลล์สคริปต์changeallซึ่งมีอินเตอร์เฟสเหมือนchangeall [-r|-R] "string1" "string2"กัน ก็จะพบไฟล์ทั้งหมดที่มีคำต่อท้ายของ.h, .C, .ccหรือ.cppและการเปลี่ยนแปลงที่เกิดขึ้นทั้งหมดให้กับstring1 เป็นตัวเลือกสำหรับการพักใน dir ปัจจุบันเท่านั้นหรือรวมถึง subdir'sstring2-r

บันทึก:

  1. สำหรับกรณีที่ไม่ซ้ำlsไม่ได้รับอนุญาตเท่านั้นที่เราสามารถใช้และfindsed
  2. ฉันพยายามแล้วfind -depthแต่ไม่รองรับ นั่นเป็นเหตุผลที่ผมสงสัยว่าถ้า-pruneจะช่วย man findแต่ไม่เข้าใจตัวอย่างจาก

แก้ไข 2: ฉันทำงานที่ได้รับมอบหมายฉันไม่ได้ถามคำถามอย่างละเอียดเพราะฉันต้องการทำมันให้เสร็จ ตั้งแต่ฉันทำไปแล้วส่งตอนนี้ฉันสามารถตอบคำถามทั้งหมดได้ นอกจากนี้ฉันจัดการจนเสร็จสิ้นการมอบหมายโดยไม่ใช้-pruneแต่ต้องการเรียนรู้ต่อไป

คำตอบ:


438

สิ่งที่ฉันพบว่าสับสน-pruneคือการกระทำ (เช่น-print) ไม่ใช่การทดสอบ (เช่น-name) มันเปลี่ยนรายการ "สิ่งที่ต้องทำ" แต่จะคืนค่าจริงเสมอ

รูปแบบทั่วไปสำหรับการใช้-pruneคือ:

find [path] [conditions to prune] -prune -o \
            [your usual conditions] [actions to perform]

คุณมักจะต้องการ-o(ตรรกะ OR) ทันทีหลังจาก-pruneนั้นเพราะส่วนแรกของการทดสอบ (จนถึงและรวมถึง-prune) จะคืนค่าเท็จสำหรับสิ่งที่คุณต้องการจริงๆ (เช่น: สิ่งที่คุณไม่ต้องการตัดออก)

นี่คือตัวอย่าง:

find . -name .snapshot -prune -o -name '*.foo' -print

จะพบไฟล์ "* .foo" ที่ไม่อยู่ในไดเรกทอรี ".snapshot" ในตัวอย่างนี้-name .snapshotทำให้ขึ้น[conditions to prune]และ-name '*.foo' -printเป็นและ[your usual conditions][actions to perform]

หมายเหตุสำคัญ :

  1. หากสิ่งที่คุณต้องการทำคือพิมพ์ผลลัพธ์ที่คุณอาจเคยชินกับการ-printกระทำ คุณมักไม่-pruneต้องการที่จะทำว่าเมื่อใช้

    พฤติกรรมเริ่มต้นของการค้นหาคือการ "และ" การแสดงออกทั้งหมดที่มีการ-printกระทำหากไม่มีการกระทำอื่นนอกเหนือจาก-prune(แดกดัน) ในตอนท้าย นั่นหมายความว่าการเขียนนี้:

    find . -name .snapshot -prune -o -name '*.foo'              # DON'T DO THIS

    เทียบเท่ากับการเขียนสิ่งนี้:

    find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS

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

    find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
  2. หาก "เงื่อนไขปกติ" ของคุณเกิดขึ้นกับไฟล์ที่ตรงกับเงื่อนไขพรุนของคุณไฟล์เหล่านั้นจะไม่รวมอยู่ในเอาต์พุต วิธีในการแก้ไขปัญหานี้คือการเพิ่มเพรดิเคตให้-type dกับสภาพพรุนของคุณ

    ตัวอย่างเช่นสมมติว่าเราต้องการที่จะตัดออกไดเรกทอรีใด ๆ ที่เริ่มต้นด้วย.git(นี้เป็นที่ยอมรับ contrived ค่อนข้าง - โดยปกติคุณจะต้องเอาสิ่งที่มีชื่อตรง .git ) .gitignoreแต่นอกเหนือจากที่อยากจะเห็นไฟล์ทั้งหมดรวมถึงไฟล์เช่น คุณอาจลอง:

    find . -name '.git*' -prune -o -type f -print               # DON'T DO THIS

    สิ่งนี้จะไม่รวม.gitignoreอยู่ในผลลัพธ์ นี่คือรุ่นที่แก้ไขแล้ว:

    find . -name '.git*' -type d -prune -o -type f -print       # DO THIS

เคล็ดลับพิเศษ: หากคุณใช้รุ่น GNU findหน้า texinfo สำหรับfindมีคำอธิบายโดยละเอียดมากกว่า manpage (เช่นเดียวกับอรรถประโยชน์ GNU ส่วนใหญ่)


6
มันไม่ชัดเจน 100% ในข้อความของคุณ (แต่เมื่อคุณพิมพ์ '* .foo' เท่านั้นจะไม่ขัดแย้งกัน) แต่ส่วน -prune จะไม่พิมพ์อะไรเลย (ไม่ใช่เฉพาะไดเรกทอรี) ที่ชื่อ ".napshot" ie, -pruneไม่เพียงทำงานบนไดเร็กตอรี่ (แต่, สำหรับไดเร็กตอรี่, มันยังป้องกันการป้อนไดเรคทอรี่ที่ตรงกับเงื่อนไขนั้น, เช่นนี่คือ dirs ที่ตรงกัน-name .snapshot)
Olivier Dulac

12
และ +1 สำหรับคุณสำหรับคำอธิบายที่ทำไว้อย่างดี (และโดยเฉพาะอย่างยิ่งบันทึกย่อที่สำคัญ) คุณควรส่งสิ่งนี้ไปยังผู้พัฒนาที่ค้นพบ (เนื่องจากหน้าคนไม่ได้อธิบายว่า "ลูกพรุน" สำหรับมนุษย์ทั่วไป ^^ ฉันพยายามหลายครั้งที่จะคิดออกและฉันไม่เห็นผลข้างเคียงที่คุณเตือนเราเกี่ยวกับ)
Olivier Dulac

2
@OlivierDulac เป็นจุดที่ดีมากในการลอกไฟล์ที่คุณต้องการเก็บไว้ ฉันได้อัปเดตคำตอบเพื่อชี้แจงนี้ มันไม่ใช่-pruneตัวของมันเองที่ทำให้เกิดสิ่งนี้ตามทาง ปัญหาคือว่าหรือผู้ประกอบการ "ลัดวงจร" และหรือมีความสำคัญต่ำกว่าและ ผลลัพธ์ที่ได้คือว่าถ้าไฟล์ที่เรียกว่า.snapshotจะพบว่ามันจะตรงกับครั้งแรก-name, -pruneแล้วจะทำอะไร ( แต่กลับจริง) แล้วหรือผลตอบแทนที่แท้จริงตั้งแต่ซ้ายอาร์กิวเมนต์เป็นความจริง การกระทำ (เช่น:) -printเป็นส่วนหนึ่งของการโต้แย้งที่สองดังนั้นจึงไม่เคยมีโอกาสดำเนินการ
ลอเรนซ์ Gonsalves

3
1 ในที่สุดก็พบว่าทำไมฉันต้อง-printในตอนท้าย, ตอนนี้ผมสามารถหยุดการเพิ่ม\! -path <pattern>นอกเหนือไป-prune
ตัวแปรอนาถ

6
โปรดทราบว่า "-o" เป็นชวเลขสำหรับ "- หรือ" ซึ่ง (ในขณะที่ไม่เป็นไปตาม POSIX) จะอ่านได้ชัดเจนยิ่งขึ้น
yoyo

27

โดยปกติแล้ววิธีดั้งเดิมที่เราทำใน linux และวิธีที่เราคิดว่าเป็นจากซ้ายไปขวา
ดังนั้นคุณจะไปและเขียนสิ่งที่คุณกำลังมองหาก่อน:

find / -name "*.php"

จากนั้นคุณอาจกด Enter และรับรู้ว่าคุณได้รับไฟล์มากเกินไปจากไดเรกทอรีที่คุณไม่ต้องการ ลองแยก / สื่อเพื่อหลีกเลี่ยงการค้นหาไดรฟ์ที่ติดตั้งของคุณ
ตอนนี้คุณควรเพิ่มต่อไปนี้กับคำสั่งก่อนหน้า:

-print -o -path '/media' -prune

ดังนั้นคำสั่งสุดท้ายคือ:

find / -name "*.php" -print -o -path '/media' -prune

............... | <--- รวม ---> | .................... | <- -------- ยกเว้น ---------> |

ฉันคิดว่าโครงสร้างนี้ง่ายกว่าและสัมพันธ์กับแนวทางที่ถูกต้อง


3
ฉันจะไม่คาดหวังว่าสิ่งนี้จะมีประสิทธิภาพ - ฉันคิดว่ามันจะประเมินประโยคซ้ายก่อนลูกพรุน แต่ด้วยความประหลาดใจของฉันการทดสอบอย่างรวดเร็วดูเหมือนจะแนะนำว่าfindฉลาดพอที่จะประมวลผล-pruneประโยคแรก อืมน่าสนใจ
artfulrobot

ฉันไม่เคยคิดเลยว่าจะใช้ GNU find เกือบทศวรรษ! ขอบคุณสำหรับสิ่งนั้น! มันจะเปลี่ยนวิธีที่ฉันคิดอย่างแน่นอนต่อ-pruneจากนี้ไป
เฟลิเป้อัลวาเรซ

3
@artfulrobot มันจะประมวลผลก่อนจริงๆเหรอ? ฉันคิดว่ามันเข้ามา/mediaโดยสังเกตว่ามันไม่ได้ถูกเรียก*.phpแล้วตรวจสอบว่ามันอยู่ข้างใน/mediaหรือไม่เมื่อเห็นว่ามันเป็นดังนั้นจึงข้ามทรีย่อยทั้งหมดนั้น มันยังเหลือจากซ้ายไปขวามันแค่สร้างความแตกต่างไม่ได้ตราบใดที่เช็คทั้งสองไม่ซ้อนกัน
phk

26

ระวังว่า -prune ไม่ได้ป้องกันการลดระดับลงในไดเร็กทอรีใด ๆตามที่บางคนกล่าวไว้ มันป้องกันการลดระดับลงในไดเรกทอรีที่ตรงกับการทดสอบที่ใช้ บางทีตัวอย่างบางส่วนอาจช่วยได้ (ดูด้านล่างสำหรับตัวอย่างของ regex) ขออภัยสำหรับสิ่งนี้ที่มีความยาวมาก

$ find . -printf "%y %p\n"    # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh

$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test

$ find . -prune
.

$ find . -name test -prune
./test
./dir1/test
./dir2/test

$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl

$ find . -name test -prune -regex ".*/my.*p.$"
(no results)

$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test

$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py

$ find . -not -regex ".*test.*"                   .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

หากคุณ "สัมผัส. / dir1/scripts/test" (เช่นมีไฟล์ "ทดสอบ" และไม่ใช่ dir ในส่วนย่อยที่พิมพ์ออกมา) มันจะไม่ได้รับ printd โดยfind . -name test -prune -o -print: iow -pruneคือการกระทำที่ ทำงานกับไฟล์ได้
Olivier Dulac

10

การเพิ่มคำแนะนำที่ให้ไว้ในคำตอบอื่น ๆ (ฉันไม่มีตัวแทนในการสร้างคำตอบ) ...

เมื่อรวม-pruneกับนิพจน์อื่น ๆ จะมีความแตกต่างเล็กน้อยในพฤติกรรมขึ้นอยู่กับการใช้นิพจน์อื่น

ตัวอย่าง @Laurence Gonsalves จะพบไฟล์ "* .foo" ที่ไม่ได้อยู่ในไดเรกทอรี ".snapshot": -

find . -name .snapshot -prune -o -name '*.foo' -print

อย่างไรก็ตามนี่อาจเป็นรายการสั้น ๆ ที่แตกต่างกันเล็กน้อยโดยไม่ได้ตั้งใจอาจแสดงรายการ.snapshotไดเรกทอรี (และไดเรกทอรี. snapshot ที่ซ้อนกัน): -

find . -name .snapshot -prune -o -name '*.foo'

เหตุผลคือ (ตาม manpage ในระบบของฉัน): -

หากการแสดงออกที่กำหนดไม่ได้มีใด ๆ ของพรรค -exec, -ls, -ok หรือ -print การแสดงออกที่กำหนดจะถูกแทนที่อย่างมีประสิทธิภาพโดย:

(Given_expression) - พิมพ์

นั่นคือตัวอย่างที่สองนั้นเทียบเท่ากับการป้อนสิ่งต่อไปนี้ดังนั้นการแก้ไขการจัดกลุ่มคำ: -

find . \( -name .snapshot -prune -o -name '*.foo' \) -print

สิ่งนี้มีให้เห็นอย่างน้อยใน Solaris 5.10 หลังจากใช้รสชาติต่าง ๆ ของ * ระวังที่ประมาณ 10 ปีฉันเพิ่งค้นหาเหตุผลว่าทำไมสิ่งนี้เกิดขึ้น


ขอขอบคุณสำหรับความสนใจในความแตกต่างระหว่างการใช้ทั้งที่-pruneมีและไม่มี-print!
mcw

3

ลูกพรุนจะไม่หัก ณ ที่จ่ายใด ๆ

จากหน้าคน

หากไม่ได้รับความลึก - ความจริง; หากไฟล์เป็นไดเร็กทอรีห้ามลงมาในไฟล์ หากได้รับความลึกเป็นเท็จ ไม่มีผลกระทบ.

โดยทั่วไปมันจะไม่เข้าสู่ไดเรกทอรีย่อยใด ๆ

ใช้ตัวอย่างนี้:

คุณมีไดเรกทอรีดังต่อไปนี้

  • / home / test2
  • / home / test2 / test2

หากคุณทำงานfind -name test2:

มันจะส่งคืนทั้งสองไดเรกทอรี

หากคุณทำงานfind -name test2 -prune:

มันจะส่งกลับเท่านั้น / home / test2 ตามที่จะไม่ลงมา / home / test2 เพื่อค้นหา / home / test2 / test2


ไม่ใช่ 100% จริง: มันคือ "ทำการตัดเมื่อเงื่อนไขการจับคู่และถ้ามันเป็นไดเรกทอรีให้นำออกจากรายการสิ่งที่ต้องทำคือไม่ป้อนเช่นกัน" -prune ยังทำงานกับไฟล์ได้อีกด้วย
Olivier Dulac

2

ฉันไม่มีความเชี่ยวชาญในเรื่องนี้ (และหน้านี้มีประโยชน์มากพร้อมกับhttp://mywiki.wooledge.org/UsingFind )

เพียงแค่สังเกตุเห็นว่า-pathเป็นเส้นทางที่ตรงกับสตริง / พา ธ ที่ตามหลังfind ( .ในตัวอย่างเหล่านี้) ซึ่ง-nameตรงกับชื่อฐานทั้งหมด

find . -path ./.git  -prune -o -name file  -print

บล็อกไดเรกทอรี .git ในไดเรกทอรีปัจจุบันของคุณ(เป็นการค้นพบของคุณ. )

find . -name .git  -prune -o -name file  -print

บล็อกไดเรกทอรีย่อย. git ทั้งหมดซ้ำ ๆ

หมายเหตุ ./ สำคัญมาก !! -pathจะต้องตรงกับเส้นทางที่ทอดยาวไป. หรืออะไรก็ตามหลังจากค้นหาถ้าคุณได้แมทช์กับมัน (จากอีกด้านหนึ่งของหรือ ' -o') อาจจะไม่มีการตัดแต่ง! ฉันไม่รู้เรื่องนี้อย่างไร้เดียงสาและมันทำให้ฉันใช้ -path เมื่อมันยอดเยี่ยมเมื่อคุณไม่ต้องการที่จะตัดไดเรกทอรีย่อยทั้งหมดด้วย basename เดียวกัน: D


หมายเหตุหากการพูดของfind bla/คุณคุณจะต้อง -path bla/.git (หรือถ้าคุณกด*ที่ด้านหน้าแทนมันจะทำงานเหมือน -name)
sabgenton


0

หากคุณอ่านคำตอบที่ดีทั้งหมดที่นี่ความเข้าใจของฉันในตอนนี้คือสิ่งต่อไปนี้ให้ผลลัพธ์ที่เหมือนกันทั้งหมด:

find . -path ./dir1\*  -prune -o -print

find . -path ./dir1  -prune -o -print

find . -path ./dir1\*  -o -print
#look no prune at all!

แต่อันสุดท้ายจะใช้เวลานานกว่ามากเนื่องจากมันยังค้นหาทุกอย่างใน dir1 ฉันเดาว่าคำถามจริงคือทำอย่างไร-orกำจัดผลลัพธ์ที่ไม่ต้องการโดยไม่ต้องค้นหาพวกเขาจริง ๆ

ดังนั้นฉันคิดว่าลูกพรุนหมายถึงไม่ตรงกับที่ผ่านมา แต่ทำเครื่องหมายว่าทำ ...

http://www.gnu.org/software/findutils/manual/html_mono/find.html "อย่างไรก็ตามนี่ไม่ได้เกิดจากผลของการกระทำ '-prune' (ซึ่งป้องกันการสืบเชื้อสายเพิ่มเติมเท่านั้น) เราไม่สนใจรายการนั้น) แต่เอฟเฟกต์นี้เกิดจากการใช้ '-o' เนื่องจากด้านซ้ายมือของเงื่อนไข“ หรือ” สำเร็จสำหรับ. /src/emacs จึงไม่จำเป็นต้องประเมินสิทธิ์ - ด้านข้าง ('-print') สำหรับไฟล์นี้โดยเฉพาะ "


0

findสร้างรายการของไฟล์ มันใช้คำกริยาที่คุณให้กับแต่ละคนและส่งกลับผู้ที่ผ่าน

แนวคิดนี้-pruneหมายความว่าการแยกออกจากผลลัพธ์ทำให้ฉันสับสนจริงๆ คุณสามารถยกเว้นไฟล์โดยไม่ต้องตัดพรุน:

find -name 'bad_guy' -o -name 'good_guy' -print  // good_guy

ทั้งหมด-pruneจะเปลี่ยนพฤติกรรมของการค้นหา หากการจับคู่ปัจจุบันเป็นไดเรกทอรีจะบอกว่า"เฮ้findไฟล์ที่คุณเพิ่งจับคู่ไม่ได้ลงไป"ไฟล์ที่คุณเพิ่งจับคู่อย่าลงไปมัน"มันเพียงลบทรีนั้น (แต่ไม่ใช่ไฟล์เอง) จากรายการไฟล์ที่ต้องการค้นหา

-dont-descendมันควรจะตั้งชื่อ


0

มีคำตอบค่อนข้างน้อย บางคนมีทฤษฎีหนักมากเกินไป ฉันจะออกไปว่าทำไมฉันถึงต้องมีการตัดครั้งเดียวบางทีอาจเป็นตัวอย่างที่ต้องการก่อนคำอธิบายประเภทเป็นประโยชน์กับใครบางคน :)

ปัญหา

ฉันมีโฟลเดอร์ที่มีไดเรกทอรีโหนดประมาณ 20 แห่งแต่ละแห่งมี node_modulesไดเรกทอรีตามที่คาดไว้

เมื่อคุณเข้าสู่โครงการใด ๆ คุณจะเห็นแต่ละ../node_modules/moduleโครงการ แต่คุณรู้ว่ามันเป็นอย่างไร เกือบทุกโมดูลมีการขึ้นต่อกันดังนั้นสิ่งที่คุณกำลังดูเป็นเหมือนprojectN/node_modules/moduleX/node_modules/moduleZ...

ฉันไม่ต้องการจมกับรายการที่ขึ้นกับการพึ่งพาของ ...

รู้-d n/ -depth nมันจะไม่ช่วยฉันเพราะไดเรกทอรี node_modules หลัก / แรกที่ฉันต้องการของแต่ละโครงการมีความลึกที่แตกต่างกันเช่นนี้

Projects/MysuperProjectName/project/node_modules/...
Projects/Whatshisname/version3/project/node_modules/...
Projects/project/node_modules/...
Projects/MysuperProjectName/testProject/november2015Copy/project/node_modules/...
[...]

ฉันจะรับรายการเส้นทางแรกที่ลงท้ายด้วยแรกnode_modulesและย้ายไปยังโครงการถัดไปเพื่อให้เหมือนกันได้อย่างไร

เข้าสู่ -prune

เมื่อคุณเพิ่ม-pruneคุณจะยังคงมีการค้นหาแบบเรียกซ้ำมาตรฐาน "เส้นทาง" แต่ละเส้นทางจะได้รับการวิเคราะห์และการค้นหาทุกครั้งจะคายออกมาและfindขุดลงมาอย่างต่อเนื่อง แต่มันเป็นการขุดเพื่อnode_modulesสิ่งที่ฉันไม่ต้องการ

ดังนั้นความแตกต่างคือในเส้นทางที่แตกต่างกันเหล่านั้น-pruneจะfindหยุดการขุดลงไปตามถนนสายนั้นเมื่อพบรายการของคุณ ในกรณีของฉันnode_modulesโฟลเดอร์

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