grep สำหรับหลาย ๆ สตริงในไฟล์ในบรรทัดที่แตกต่างกัน (เช่นไฟล์ทั้งหมดไม่ใช่การค้นหาตามบรรทัด)?


85

ฉันต้องการที่จะ grep สำหรับไฟล์ที่มีคำDansk, Svenskaหรือ Norskบนเส้นใด ๆ กับรหัสผลลัพธ์ที่ใช้งาน (ที่ผมชอบจริงๆเท่านั้นที่จะมีข้อมูลที่สตริงที่มีอยู่, หนึ่งซับของฉันไปเล็ก ๆ น้อย ๆ อีกแล้วนี้)

ฉันมีไฟล์จำนวนมากที่มีบรรทัดในลักษณะนี้:

Disc Title: unknown
Title: 01, Length: 01:33:37.000 Chapters: 33, Cells: 31, Audio streams: 04, Subpictures: 20
        Subtitle: 01, Language: ar - Arabic, Content: Undefined, Stream id: 0x20, 
        Subtitle: 02, Language: bg - Bulgarian, Content: Undefined, Stream id: 0x21, 
        Subtitle: 03, Language: cs - Czech, Content: Undefined, Stream id: 0x22, 
        Subtitle: 04, Language: da - Dansk, Content: Undefined, Stream id: 0x23, 
        Subtitle: 05, Language: de - Deutsch, Content: Undefined, Stream id: 0x24, 
(...)

นี่คือรหัสเทียมของสิ่งที่ฉันต้องการ:

for all files in directory;
 if file contains "Dansk" AND "Norsk" AND "Svenska" then
 then echo the filename
end

วิธีที่ดีที่สุดในการทำคืออะไร? สามารถทำได้ในบรรทัดเดียวหรือไม่?

คำตอบ:


89

คุณสามารถใช้ได้:

grep -l Dansk * | xargs grep -l Norsk | xargs grep -l Svenska

หากคุณต้องการค้นหาในไฟล์ที่ซ่อนอยู่:

grep -l Dansk .* | xargs grep -l Norsk | xargs grep -l Svenska

วิธีแก้ปัญหาที่ชาญฉลาด สิ่งหนึ่งที่ควรทราบ (โดยทั่วไปจะพูดไม่เกี่ยวข้องกับสิ่งที่ OP ขอ) คือรหัสทางออกโดยรวมจะเป็น0แม้ในกรณีที่ (แนวคิด) ล้มเหลว ดังนั้นหากคุณสนใจที่จะพิจารณาความล้มเหลวกับความสำเร็จคุณอาจต้องตรวจสอบว่าเอาต์พุต stdout ว่างเปล่าหรือไม่หรือใช้แนวทางของ @ EddSteel แทน
mklement0

@mklement: ใน Bash PIPESTATUSอาร์เรย์มีค่าออกของสมาชิกของไปป์ไลน์
Dennis Williamson

@DennisWilliamson ดีที่ได้รู้ขอบคุณ อีกทางเลือกหนึ่งคือการเปิดpipefailตัวเลือกเชลล์ (ชั่วคราว):shopt -so pipefail
mklement0

4
คุณอาจต้องการใช้grep -Zและxargs -0หากชื่อไฟล์ของคุณมีช่องว่างได้
Ben Challenor

1
ซึ่งอาจทำให้เกิดข้อผิดพลาด "รายการอาร์กิวเมนต์ยาวเกินไป" หากคุณมีไฟล์จำนวนมาก
AnnanFay

23

อีกวิธีหนึ่งโดยใช้เพียง bash และ grep:

สำหรับไฟล์ 'test.txt' ไฟล์เดียว:

  grep -q Dansk test.txt && grep -q Norsk test.txt && grep -l Svenska test.txt

จะพิมพ์test.txtiff ไฟล์มีทั้งสาม (รวมกัน) greps สองตัวแรกไม่พิมพ์อะไรเลย ( -q) และตัวสุดท้ายจะพิมพ์เฉพาะไฟล์เท่านั้น

หากคุณต้องการทำสำหรับทุกไฟล์ในไดเร็กทอรี:

   สำหรับ f in *; ทำ grep -q Dansk $ f && grep -q Norsk $ f && grep -l Svenska $ f; เสร็จแล้ว

แต่ไม่จำเป็นต้องเรียกใช้ grep 3 ครั้ง
kurumi

1
ฉันรู้ว่าคุณสามารถรวมรูปแบบกับ -e ได้ แต่ฉันมองไม่เห็นวิธีการรวมเป็น grep เพียงอย่างเดียว
Edd Steel

1
เยี่ยมมาก; re for f ...: ใช้"$f"(double-quoting) แทนที่จะเป็นเพียง$fเพื่อให้แน่ใจว่าชื่อไฟล์ที่มีช่องว่างฝัง ฯลฯ ได้รับการจัดการอย่างถูกต้อง
mklement0

ข้อได้เปรียบของวิธีนี้เหนือ @ vmpstr's คือรหัสออกจะแสดงอย่างถูกต้องว่ามีคำค้นหาทั้งหมดที่พบหรือไม่
mklement0

19
grep –irl word1 * | grep –il word2 `cat -` | grep –il word3 `cat -`
  • -i ทำให้ไม่คำนึงถึงกรณีการค้นหา
  • -r ทำให้การค้นหาไฟล์เรียกซ้ำผ่านโฟลเดอร์
  • -l ท่อรายการไฟล์ที่มีคำที่พบ
  • cat - ทำให้ grep ถัดไปมองผ่านไฟล์ที่ส่งไปยังรายการ

1
นี่คือคำตอบที่ง่ายและตรงไปตรงมาที่สุดขอบคุณที่เป็นประโยชน์มาก!
majick

9

วิธี grep สำหรับหลาย ๆ สตริงในไฟล์ในบรรทัดต่างๆ (ใช้สัญลักษณ์ไปป์):

for file in *;do 
   test $(grep -E 'Dansk|Norsk|Svenska' $file | wc -l) -ge 3 && echo $file
done

หมายเหตุ:

  1. หากคุณใช้เครื่องหมายคำพูดคู่""กับ grep ของคุณคุณจะต้องหนีไปป์แบบนี้\|เพื่อค้นหา Dansk, Norsk และ Svenska

  2. สมมติว่าหนึ่งบรรทัดมีเพียงภาษาเดียว

บทสรุป: http://www.cyberciti.biz/faq/howto-use-grep-command-in-linux-unix/


จะไม่ล้มเหลวถ้า Dansk Norsk และ Svenska ทั้งหมดปรากฏในบรรทัดเดียวกัน?
vmpstr

ใช่มันจะล้มเหลวในกรณีนั้น ฉันคิดว่าภาษาจะปรากฏขึ้นหนึ่งภาษาต่อบรรทัด
Damodharan R

นอกจากนี้ยังจะยื่นถ้าฉันมีเพียงอย่างเดียวNorskแต่มีสามบรรทัดที่แตกต่างกัน
Benjamin W.

6

คุณสามารถทำได้อย่างง่ายดายด้วยack :

ack -l 'cats' | ack -xl 'dogs'
  • -l: ส่งคืนรายการไฟล์
  • -x: นำไฟล์จาก STDIN (การค้นหาก่อนหน้า) และค้นหาเฉพาะไฟล์เหล่านั้น

และคุณสามารถเดินท่อไปเรื่อย ๆ จนกว่าจะได้ไฟล์ที่ต้องการ


Unknown option: xเมื่อฉันพยายามนี้ก็กล่าวว่า มี ack บางรุ่นที่รองรับแฟล็ก x นี้หรือไม่?
Hassan

4
awk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print "0" }' 

จากนั้นคุณสามารถจับค่าส่งคืนด้วยเชลล์

ถ้าคุณมี Ruby (1.9+)

ruby -0777 -ne 'print if /Dansk/ and /Norsk/ and /Svenka/' file

1
ในประโยค END ของ awk คุณอาจต้องการ: if (a && b && c) {exit 0} else {exit 1}หรือมากกว่านั้นอย่างรวดเร็วexit !(a && b && c)
glenn jackman

ทับทิมของคุณดูไม่ถูกต้อง ที่จะพิมพ์เฉพาะย่อหน้าที่มีคำค้นหาทั้งหมด คำถามคือไฟล์ (โดยรวม) มีคำทั้งหมดหรือไม่แม้ว่าทั้งหมดจะไม่ปรากฏในย่อหน้าเดียวกันก็ตาม
glenn jackman

ขอบคุณ. เปลี่ยนถ้าต้องการทั้งไฟล์ก็ต้องใช้ -0777
kurumi

4

สิ่งนี้ค้นหาคำหลายคำในหลายไฟล์:

egrep 'abc|xyz' file1 file2 ..filen 

2
นอกเหนือจากการค้นหาไฟล์ที่มีทั้งสองสตริงแล้วยังพบไฟล์ที่มี 'abc' หรือ 'xyz' เพียงอย่างเดียว ฉันคิดว่า OP กำลังขอไฟล์ที่มี "abc" และ "xyz"
Chris Warth

3

เพียงแค่:

grep 'word1\|word2\|word3' *

ดู โพสต์นี้สำหรับข้อมูลเพิ่มเติม


ฉันจะเพิ่ม-lค่าสถานะ แต่นอกเหนือจากนั้นคำตอบนี้ดูเหมือนจะตรงไปตรงมาที่สุดสำหรับฉันเว้นแต่ฉันจะขาดอะไรไป
xdhmoore

ใช่มันยังมีประสิทธิภาพมากขึ้นเนื่องจากคุณไม่ได้ประมวลผลข้อมูลทั้งหมดภายในท่อและตัวกรองหลายตัว
moshe beeri

3
คำถามถามเกี่ยวกับนิพจน์ที่ส่งคืนไฟล์ที่มีทั้งสามคำ สิ่งนี้จะส่งคืนบรรทัด (แทนชื่อไฟล์) ที่มีหนึ่งในสาม (แทนที่จะเป็นทั้งสาม)
Benjamin W.

2

นี่คือการผสมผสานระหว่างคำตอบของเกล็นแจ็คแมนและคุรุมิซึ่งอนุญาตให้ใช้ regexes ได้ตามอำเภอใจแทนที่จะเป็นจำนวนคำที่กำหนดโดยพลการหรือชุด regexes คงที่

#!/usr/bin/awk -f
# by Dennis Williamson - 2011-01-25

BEGIN {
    for (i=ARGC-2; i>=1; i--) {
        patterns[ARGV[i]] = 0;
        delete ARGV[i];
    }
}

{
    for (p in patterns)
        if ($0 ~ p)
            matches[p] = 1
            # print    # the matching line could be printed
}

END {
    for (p in patterns) {
        if (matches[p] != 1)
            exit 1
    }
}

เรียกใช้ดังนี้:

./multigrep.awk Dansk Norsk Svenska 'Language: .. - A.*c' dvdfile.dat

2

นี่คือสิ่งที่ทำงานได้ดีสำหรับฉัน:

find . -path '*/.svn' -prune -o -type f -exec gawk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print FILENAME }' {} \;
./path/to/file1.sh
./another/path/to/file2.txt
./blah/foo.php

ถ้าฉันแค่ต้องการค้นหาไฟล์. sh ที่มีทั้งสามไฟล์ฉันสามารถใช้

find . -path '*/.svn' -prune -o -type f -name "*.sh" -exec gawk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print FILENAME }' {} \;
./path/to/file1.sh

1

ขยายคำตอบ awk ของ @ kurumi ต่อไปนี้เป็นฟังก์ชันทุบตี:

all_word_search() {
    gawk '
        BEGIN {
            for (i=ARGC-2; i>=1; i--) {
                search_terms[ARGV[i]] = 0;
                ARGV[i] = ARGV[i+1];
                delete ARGV[i+1];
            }
        }
        {
            for (i=1;i<=NF; i++) 
                if ($i in search_terms) 
                    search_terms[$1] = 1
        }
        END {
            for (word in search_terms) 
                if (search_terms[word] == 0) 
                    exit 1
        }
    ' "$@"
    return $?
}

การใช้งาน:

if all_word_search Dansk Norsk Svenska filename; then
    echo "all words found"
else
    echo "not all words found"
fi

1

ฉันทำอย่างนั้นด้วยสองขั้นตอน สร้างรายการไฟล์ csv ในไฟล์เดียวด้วยความช่วยเหลือของความคิดเห็นในหน้านี้ฉันทำสองขั้นตอนแบบไม่ใช้สคริปต์เพื่อให้ได้สิ่งที่ฉันต้องการ เพียงพิมพ์ลงในเทอร์มินัล:

$ find /csv/file/dir -name '*.csv' > csv_list.txt
$ grep -q Svenska `cat csv_list.txt` && grep -q Norsk `cat csv_list.txt` && grep -l Dansk `cat csv_list.txt`

มันทำในสิ่งที่ฉันต้องการ - พิมพ์ชื่อไฟล์ที่มีทั้งสามคำ

นอกจากนี้ยังคำนึงถึงสัญลักษณ์เช่น `' "


1

หากคุณต้องการเพียงสองคำค้นหาเนื้อหาแนวทางที่อ่านได้ง่ายที่สุดคือเรียกใช้การค้นหาแต่ละรายการและตัดผลลัพธ์:

 comm -12 <(grep -rl word1 . | sort) <(grep -rl word2 . | sort)

1

หากคุณติดตั้งคอมไพล์

git grep -l --all-match --no-index -e Dansk -e Norsk -e Svenska

- ไม่มีดัชนีค้นหาไฟล์ในไดเร็กทอรีปัจจุบันที่ไม่ได้รับการจัดการโดย Git ดังนั้นคำสั่งนี้จะทำงานในไดเร็กทอรีใด ๆ ไม่ว่าจะเป็นที่เก็บ git หรือไม่ก็ตาม


0

วันนี้ฉันมีปัญหานี้และ one-liners ทั้งหมดที่นี่ล้มเหลวสำหรับฉันเพราะไฟล์มีช่องว่างในชื่อ

นี่คือสิ่งที่ฉันคิดขึ้นมาซึ่งได้ผล:

grep -ril <WORD1> | sed 's/.*/"&"/' | xargs grep -il <WORD2>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.