วิธีการใช้ grep ในไฟล์ทั้งหมดที่ไม่ใช่แบบเรียกซ้ำในไดเรกทอรี?


34

ฉันต้องการค้นหาสตริงข้อความในไฟล์ทั้งหมดในไดเรกทอรี (และไม่ใช่ไดเรกทอรีย่อยของมันฉันรู้ว่า-rตัวเลือกทำเช่นนั้น แต่นั่นไม่ใช่สิ่งที่ฉันต้องการ)

  1. วิ่ง

    grep "string" /path/to/dir
    

    ฉันควรจะทำสิ่งนี้ได้ฉันอ่าน แต่มันทำให้ฉันมีข้อผิดพลาด:

    grep: dir: เป็นไดเรกทอรี

  2. ต่อไปฉันลองใช้grepไฟล์หลาย ๆ ไฟล์

    grep "string" .bashrc .bash_aliases ทำงานได้อย่างสมบูรณ์แบบ

    grep "string" .bash* ทำงานตามที่ตั้งใจไว้ด้วย

    grep "string" * ให้ฉันข้อผิดพลาด:

    grep: data: Is a directory
    grep: Desktop: Is a directory
    grep: Documents: Is a directory
    grep: Downloads: Is a directory
    ...
    

พิมพ์เฉพาะข้อผิดพลาดฉันไม่ได้เส้นที่ตรงกัน ฉันพยายามใช้-sตัวเลือก แต่ไม่มีประโยชน์

ดังนั้นคำถามของฉัน:

  1. เหตุใดฉันจึงไม่สามารถใช้grepในไดเรกทอรีเช่นเดียวกับใน (1) เมื่อฉันควรจะสามารถทำได้ ฉันเห็นว่าทำในตัวอย่างมากมายบนอินเทอร์เน็ต
    แก้ไข : เมื่อฉันพูดว่า "ใช้ grep ในไดเรกทอรี" ฉันหมายถึง "ค้นหาไฟล์ทั้งหมดในไดเรกทอรีนั้นซึ่งไม่รวมไดเรกทอรีย่อย" ฉันเชื่อว่านี่คือสิ่งที่ grep ทำเมื่อคุณส่งไดเรกทอรีไปยังไฟล์แทนไฟล์ ฉันไม่ถูกต้องหรือไม่

  2. กรุณาให้คำอธิบายเกี่ยวกับงานgrepที่จะอธิบายพฤติกรรมของคำสั่งใน (2)
    แก้ไข : ให้ฉันเจาะจงมากขึ้น ทำไมไม่ใช้สัญลักษณ์เพื่อระบุหลายไฟล์เพื่อค้นหาในสำหรับการทำงานกับ.bash*และไม่ได้มี*หรือแม้กระทั่ง./*?

  3. ฉันจะค้นหาไฟล์ทั้งหมดในไดเรกทอรี (และไม่ใช่ไดเรกทอรีย่อย) โดยใช้grepอย่างไร


นอกจากนี้คุณยังต้องพึ่งพาเชลล์ที่ขยายสัญลักษณ์แทนเช่น*รู้จักกันในชื่อ globbing Globbing ไม่รวมชื่อไฟล์ที่ขึ้นต้นด้วยจุดเช่น.bashrcมาตรฐาน คุณสามารถตั้งค่าตัวเลือกของเชลล์เพื่อที่จะรวมไฟล์เหล่านี้ แต่คุณสามารถทำให้ตัวเองยุ่งเหยิงหากคุณไม่รู้ว่าคุณกำลังทำอะไรอยู่ คำแนะนำที่ดีสำหรับการทำความเข้าใจ globing สามารถพบได้ที่นี่mywiki.wooledge.org/glob
Arronical

ฉันไม่รู้ว่าทำไม แต่ฉันมักจะทำอะไรกับไฟล์ที่ซ่อนอยู่เสมอและมันก็ใช้ได้เสมอ ฉันไม่ได้เปลี่ยนการตั้งค่าหรืออะไร ขณะที่ฉันชี้ไปที่ (2) มันก็ใช้ได้ดีgrep "string" .bash*เช่นกัน
John Red

ขออภัยตัวอย่างสุดท้ายของฉันไม่ถูกต้อง คุณสามารถค้นหาไฟล์ที่ซ่อนอยู่ได้เช่นกันและระงับ "is a directory" เนื่องจาก Linux จะเห็นว่าไดเรกทอรีเป็นไฟล์ประเภทอื่น คำสั่งนั้นจะเป็น: grep "string" * .* 2>/dev/nullหรือgrep -s "string" * .*
Terrance

คำตอบ:


40

ในทุบตี glob จะไม่ขยายเป็นไฟล์ที่ซ่อนอยู่ดังนั้นหากคุณต้องการค้นหาทุกแฟ้มในไดเรกทอรีคุณต้องระบุไฟล์ที่ซ่อนอยู่และไม่ซ่อน.**

เพื่อหลีกเลี่ยงข้อผิดพลาด "เป็นไดเรกทอรี" คุณสามารถใช้-d skipแต่ในระบบของฉันฉันยังได้รับข้อผิดพลาดgrep: .gvfs: Permission deniedดังนั้นฉันแนะนำให้ใช้-sซึ่งจะซ่อนข้อความแสดงข้อผิดพลาดทั้งหมด

ดังนั้นคำสั่งที่คุณต้องการคือ:

grep -s "string" * .*

หากคุณกำลังค้นหาไฟล์ใน dir อื่น:

grep -s "string" /path/to/dir/{*,.*}

อีกตัวเลือกหนึ่งคือการใช้dotglobตัวเลือกของเชลล์ซึ่งจะทำให้กลมมีไฟล์ที่ซ่อนอยู่

shopt -s dotglob
grep -s "string" *

สำหรับไฟล์ใน dir อื่น:

grep -s "string" /path/to/dir/*

†มีคนพูดว่าฉันไม่ควรได้รับข้อผิดพลาดนี้ พวกเขาอาจจะถูกต้อง - ฉันอ่าน แต่ก็ไม่สามารถทำหัวหรือก้อยได้


มีเหตุผลสำหรับช่องว่างระหว่าง*และ.*?
Hashim

2
@Hashim เปรียบเทียบเอาต์พุตecho * .*และecho *.*รันในโฮมไดเร็กตอรี่ของคุณ, และความแตกต่างควรชัดเจน. มิฉะนั้น LMK และฉันจะอธิบาย
wjandrea

ที่น่าสนใจเพื่อecho *แสดงไฟล์และโฟลเดอร์ที่echo *.*ไม่ได้ซ่อนecho .*แสดงไฟล์ที่ไม่ได้ซ่อนแสดงไฟล์ทั้งหมดและecho * .*แสดงไฟล์และไดเรกทอรีทั้งหมด แต่ทำไมเหตุผลสำหรับช่องว่างระหว่างทั้งสองในกรณีหลัง? มันทำให้ฉันรู้สึกยุ่ง ไม่มีวิธีรวมทั้งสองอย่างเข้าด้วยกันเพื่อให้ได้ผลลัพธ์เดียวกันหรือไม่ หรือมิฉะนั้นจะมีคำอธิบายทางไวยากรณ์ว่าทำไมทั้งสองจึงต้องแยกจากกันที่นี่หรือเป็น* .*กรณีพิเศษ?
Hashim

1
@Hashim ฉันไม่แน่ใจว่าคุณมาถึงข้อสรุปเหล่านั้นได้อย่างไรให้ฉันอธิบาย ก่อนไดเรกทอรีเป็นไฟล์ในบริบทนี้ ใน globs *หมายถึงไฟล์ที่ไม่ได้ซ่อนไว้ทั้งหมด (เช่นชื่อไฟล์ที่ไม่ได้ขึ้นต้นด้วยจุด); .*หมายถึงไฟล์ที่ซ่อนทั้งหมด (เช่นชื่อไฟล์ที่จะเริ่มต้นด้วยจุด); และ*.*แสดงถึงไฟล์ที่ไม่ได้ซ่อนไว้ซึ่งมีจุด ในecho * .*globs ทั้งสองจะต้องแยกจากกันเพราะพวกเขาแตกต่างกัน globs: หนึ่งสำหรับที่ไม่ซ่อนตัวหนึ่งสำหรับซ่อนอยู่ แม้ว่าอย่างที่ฉันเขียนไว้ในคำตอบของฉันคุณสามารถสร้าง*รวมไฟล์ที่ซ่อนไว้โดยการเปิดdotglobตัวเลือกเชลล์
wjandrea

1
การใช้งาน*.*ทั่วไปบน Windows (DOS) เป็นวิธีการแสดงรายการไฟล์ทั้งหมด แต่ใน * nix จะรวมเฉพาะไฟล์ที่มีจุดในไฟล์เท่านั้นดังนั้นจึงไม่สมเหตุสมผลกับ * nix คุณใช้*เพื่อแสดงรายการไฟล์ทั้งหมดยกเว้นไฟล์ที่ซ่อนอยู่และ.*เพื่อแสดงรายการไฟล์ที่ซ่อนอยู่
thomasrutter

10

คุณต้อง-d skipเพิ่มตัวเลือกใน

  1. Grep กำลังค้นหาภายในไฟล์ คุณสามารถค้นหาแบบวนซ้ำได้ถ้าคุณต้องการค้นหาไฟล์ในไดเรกทอรี

  2. ตามค่าเริ่มต้น grep จะอ่านไฟล์ทั้งหมดและตรวจพบไดเรกทอรี เพราะโดยปกติแล้วคุณยังไม่ได้กำหนดว่าจะทำอย่างไรกับไดเร็กตอรี่ที่มี-dตัวเลือกมันจะให้เอาต์พุตข้อผิดพลาด

  3. การค้นหาภายในไดเรกทอรีหลักจะเป็น `grep -d skip" string "./*


สำหรับข้อมูลเพิ่มเติมเกี่ยว grep man grepดู
ไม่ระบุชื่อ 2

(a) โปรดดูการแก้ไข (b) การใช้-d skipไม่ทำงาน มันเป็นพื้นเหมือนกับ-s; ดูการแก้ไขด้วย (c) grep -d skip "string" ./*ไม่ไม่ทำงานเช่นกัน
John Red

7

ตัวจับเวลาเก่าน่าจะทำสิ่งนี้:

find . -type f -print0 | xargs -0 grep "string"

3
ทำไมไม่find . -type f -exec grep string {} +?
wchargin

5
-maxdepth 1คุณยังต้องการ
wchargin

@wchargin: ถ้าคุณต้องการชื่อไฟล์ในการส่งออกเมื่อมีเพียงหนึ่งไฟล์ฉันคิดว่าคุณต้องการfind . -type f -maxdepth 1 -exec grep string /dev/null {} +
เกรกอรี่ Nisbet

1
@GregoryNisbet: ส่ง-Hต่อไปที่ grep
wchargin

2

การสร้างใหม่ - คุณต้องการ grep ไฟล์ในหนึ่งระดับของไดเรกทอรีย่อย แต่ไม่สามารถกู้คืนได้แม้ว่าไดเรกทอรีย่อยทั้งหมดหรือไม่

grep forthis  *  */*

หรือถ้าคุณไม่ต้องการไฟล์ในไดเรกทอรีปัจจุบัน

grep forthis  */*

หมายเหตุสิ่งนี้จะไม่พบไดเรกทอรีที่ขึ้นต้นด้วยจุด

grep forthis  .*/*    */*   

ควรทำงานนั้น

นอกจากนี้ยังมี-maxdepthและ-mindepthพารามิเตอร์ จำกัด ที่ใช้ได้สำหรับfindคำสั่งด้วย


จะไม่grep forthis */*ค้นหาไฟล์ทั้งในไดเรกทอรีปัจจุบันและไดเรกทอรีเดียวใช่ไหม
Hashim

@Hashim nope ส่วนใหญ่ - cos */*จับคู่สิ่งต่าง ๆ ด้วยเครื่องหมายทับเดียวเท่านั้น หากคุณมีไฟล์ชื่อa/bในไดเรกทอรีปัจจุบัน `* / * จะตรงกับที่
Criggie

0

นี่คือตัวอย่างการข้ามไดเรกทอรีโดยไม่ข้ามข้อผิดพลาดทั้งหมด:

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