เรียกใช้ "grep" ยกเว้นไฟล์ในเส้นทางที่ระบุ


12

ฉันต้องการแยกไฟล์./test/main.cppออกจากการค้นหา

นี่คือสิ่งที่ฉันเห็น:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

ฉันรู้ว่ามันเป็นไปได้ที่จะได้รับผลที่ฉันต้องการโดยใช้คำสั่งต่างๆในการจัดเรียงท่อและฟิลเตอร์ แต่มีบางข้อความ / การหลบหนีที่จะทำให้grepเข้าใจสิ่งที่ฉันต้องการกำเนิด?


โซลูชันที่ใช้การกรองเอาต์พุตไม่ได้ขยายขนาดเนื่องจากค้นหาไฟล์โดยไม่จำเป็นก่อนที่จะรวมผลลัพธ์ที่เกี่ยวข้อง ปัญหาถูกขยายถ้าฉันต้องการแยกไดเรกทอรีทั้งหมด (ด้วย--exclude-dir) นั่นเป็นเหตุผลที่ฉันต้องการให้ grep ทำการยกเว้นโดยกำเนิด
โนเบิล

1
- ไม่รวมระบุ glob ไม่เส้นทาง
PersianGulf

คำตอบ:


6

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

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+


ทำไมคุณถึงหนีออกมา\!และ\+? ดูเหมือนว่าจะทำงานได้ดีโดยไม่มีแบ็กสแลช
nobar

@nobar ฉันคุ้นเคยกับมันเพราะตัวละครบางตัวเป็นคำหลักของเชลล์ดังนั้นคุณจะไม่แปลกใจเลยเพราะไม่มีอะไรเกิดขึ้นถ้าพวกมันหลบหนี
MichalH

" grepไม่สามารถทำได้ใช้findแทน" - สมบูรณ์แบบ
โนเบิล

4

ผมไม่คิดว่ามันเป็นไปได้กับ grepGNU คุณไม่จำเป็นต้องมีท่อ

ด้วยfind:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

ด้วยzsh:

grep pattern ./**/*~./test/main.cpp(.)

(ยกเว้นไฟล์ที่ซ่อนอยู่รวมถึงการยกเว้น. git, .svn ... )


2

ฉันสามารถเขียนหนังสือ: "ศิลปะที่หายไปของxargs" การfind ... -exec … ';เปิดตัว grep สำหรับแต่ละไฟล์ (แต่ตัวแปรที่-exec … +ไม่มี) เอาละเรากำลังสูญเสียซีพียูในรอบวันเหล่านี้แล้วทำไมล่ะ แต่ถ้าประสิทธิภาพและหน่วยความจำและพลังงานเป็นปัญหา: ใช้ xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

GNU ของfind's -print0จะNUL-terminate ผลผลิตของตนและxargs' -0เกียรตินิยมตัวเลือกที่รูปแบบเป็น input สิ่งนี้ทำให้มั่นใจได้ว่าไฟล์ของคุณมีตัวละครตลกอะไรก็ตามท่อจะไม่สับสน -rตัวเลือกที่จะทำให้แน่ใจว่ามีข้อผิดพลาดในกรณีที่ยังไม่มีfindการค้นพบอะไร

หมายเหตุคุณสามารถทำสิ่งต่าง ๆ เช่น:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep ของ-zจะเป็นสิ่งเดียวกันเป็น xargs'-0


3
หมายเหตุที่น่าสนใจบางอย่าง แต่ฉันไม่แน่ใจว่าคุณถูกต้องเกี่ยวกับปัญหาด้านประสิทธิภาพ ตามที่ผมเข้าใจมันfind -exec (cmd) {} +ทำงานเหมือนกับxargsและการทำงานเช่นเดียวกับfind -exec (cmd) {} \; xargs -n1กล่าวอีกนัยหนึ่งข้อความของคุณจะถูกต้องก็ต่อเมื่อมีการ\;ใช้เวอร์ชันนั้น
โนเบิล

3
การใช้งาน Piping xargsนั้นมีประสิทธิภาพน้อยกว่าการใช้งาน-exec … +(แม้ว่าจะอยู่เล็กน้อย) -exec … \;ไม่มีคำตอบที่นี่ได้กล่าวถึง
Gilles 'ดังนั้น - หยุดความชั่วร้าย'

1
เอส - ที ฉันเดทกับตัวเอง ขอบคุณสำหรับความคิดเห็นและการแก้ไข ฉันคิดว่า \ + เป็นตัวพิมพ์ผิด โอ้ดู-exec ... +เพิ่มในเดือนมกราคม 2005 ใช่ฉันไม่ล้าสมัย ... ที่ ... ทั้งหมด
Otheus

2

หากการfindสนับสนุนของคุณ-pathซึ่งเพิ่มไปยัง POSIX ในปี 2008 แต่ยังคงหายไปใน Solaris:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +

1
ผมไม่คิดว่าจะทำงานเพราะต้องการ nobar main.cpp ในไดเรกทอรีอื่น ๆ
เอริค Renouf

1
รูปแบบของคุณจะไม่แยก main.cpp จากไดเรกทอรีอื่นทั้งหมดด้วยใช่ไหม นั่นจะไม่เป็นที่ต้องการ
Eric Renouf

@EricRenouf: โอ้ผิดของฉันอ่านผิด อัปเดตคำตอบของฉัน
cuonglm

@Gilles: ทำไม-pathไม่ POSIX?
cuonglm

อ่าขออภัยความผิดพลาดของฉันมันได้รับการเพิ่มในปี 2008 แต่ก็ยังขาดหายไปจาก Solaris
Gilles 'หยุดความชั่วร้าย'

1

สำหรับบันทึกนี่เป็นวิธีที่ฉันชอบ:

grep pattern $(find . -type f ! -path './test/main.cpp')

โดยการรักษาgrepที่จุดเริ่มต้นของคำสั่งฉันคิดว่านี่เป็นสิ่งที่ชัดเจนมากขึ้นบวกกับมันไม่ได้ปิดการใช้งานการgrepเน้นสี ในแง่หนึ่งการใช้findคำสั่งในการแทนที่คำสั่งเป็นเพียงวิธีการขยาย / แทนที่ (จำกัด ) การค้นหาไฟล์ย่อยของgrepฟังก์ชันการทำงานของ


สำหรับฉันแล้วfind -execไวยากรณ์เป็นชนิดของความลับ ความซับซ้อนอย่างหนึ่งfind -execคือ (บางครั้ง) จำเป็นสำหรับการหลีกเลี่ยงอักขระต่าง ๆ (โดยเฉพาะถ้า\;ใช้ภายใต้ Bash) เพียงเพื่อวัตถุประสงค์ในการใส่สิ่งต่าง ๆ ลงในบริบทที่คุ้นเคยคำสั่งสองคำสั่งต่อไปนี้จะเทียบเท่าโดยทั่วไป:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

หากคุณต้องการยกเว้นไดเรกทอรีย่อยคุณอาจจำเป็นต้องใช้สัญลักษณ์แทน ฉันไม่เข้าใจโครงสร้างที่นี่ - พูดคุยเกี่ยวกับความลับ :

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

หมายเหตุเพิ่มเติมอีกข้อหนึ่งสำหรับการใช้งานfindโซลูชันพื้นฐานทั่วไปสำหรับสคริปต์ : grepบรรทัดคำสั่งควรมีตัวเลือก-H/ มิฉะนั้นมันจะเปลี่ยนการแสดงผลการจัดรูปแบบภายใต้สถานการณ์ที่มีเกิดขึ้นเป็นเพียงคนเดียวที่ชื่อไฟล์ในผลการค้นหาจาก--with-filename findสิ่งนี้น่าทึ่งเพราะไม่จำเป็นถ้าใช้grepการค้นหาไฟล์ดั้งเดิม (พร้อม-rตัวเลือก)

... ยิ่งกว่านั้นคือการรวม/dev/nullเป็นไฟล์แรกในการค้นหา วิธีนี้ช่วยแก้ปัญหาสองข้อ:

  • จะช่วยให้มั่นใจว่าหากมีไฟล์เดียวที่จะค้นหาgrepจะคิดว่ามีสองไฟล์และใช้โหมดเอาต์พุตหลายไฟล์
  • จะช่วยให้มั่นใจได้ว่าหากไม่มีไฟล์ที่จะค้นหาgrepจะคิดว่ามีหนึ่งไฟล์และไม่หยุดรอ stdin

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

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')

คุณไม่ควรใช้ผลลัพธ์ของfindในการทดแทนคำสั่ง ตัวแบ่งนี้หากมีชื่อไฟล์ที่มีช่องว่างหรืออักขระพิเศษอื่น ๆ ใช้find -execมันแข็งแกร่งและใช้งานง่าย
Gilles 'ดังนั้นหยุดความชั่วร้าย'

@Gilles: จุดที่ดีมาก - นอกจากนี้ผลลัพธ์อาจเกินขีด จำกัด ขนาดบรรทัดคำสั่งของบางโปรแกรม Caveat emptor
ดี

ฮึ. ไวยากรณ์ 'find' ยากมาก '-o' เป็นตัวดำเนินการ "หรือ" (หรือ 'หรือ' บน Linux) แต่เป็นการใช้งานทั่วไป (ตัวอย่างเช่น '-prune') ไม่ได้แมปแนวความคิดกับแนวคิดของตรรกะหรือ มันเป็นฟังก์ชั่นหรือมากกว่าตรรกะหรือ
nobar

find -iname "*target*" -or -name 'exclude' -pruneอีกวิธีหนึ่งที่จะไม่รวมไดเรกทอรีย่อยขึ้นอยู่กับการจับคู่ชื่อ: มันเรียงลำดับของการทำงาน - ไดเรกทอรีที่ถูกตัดจะถูกแสดงรายการ แต่ไม่ค้นหา หากคุณไม่ต้องการให้มันอยู่ในรายการคุณสามารถผนวกส่วนที่ซ้ำซ้อน! -name 'exclude'
nobar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.