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

Розумний розчин; одне, на що слід звернути увагу (загалом кажучи; не стосується того, про що просив ОП), це те, що загальний код виходу буде рівним 0 навіть у випадку (концептуальної) невдачі. Таким чином, якщо ви зацікавлені у визначенні невдачі проти успіху, вам доведеться або перевірити, чи вихідний файл stdout порожній чи ні, або замість цього застосувати підхід @ EddSteel.
mklement0

@mklement: У Bash PIPESTATUSмасив містить вихідні значення членів конвеєра.
Призупинено до подальшого повідомлення.

@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 норвезький test.txt && grep -l Svenska test.txt

Буде надруковано, test.txtякщо файл містить усі три (у будь-якій комбінації). Перші два greps нічого не друкують ( -q), а останній друкує файл, лише якщо два інших передано.

Якщо ви хочете зробити це для кожного файлу в каталозі:

   для f у *; do grep -q Dansk $ f && grep -q Norsk $ f && grep -l Svenska $ f; зроблено

але тоді немає потреби виконувати grep 3 рази.
Курумі

1
Я знаю, що ви можете поєднувати візерунки з -e, але я не міг бачити способу створення сполучення лише в grep.
Edd Steel

1
Чудово; re for f ...: використовуйте "$f"(подвійне цитування), а не просто $fдля того, щоб забезпечити правильну обробку імен файлів із вбудованими пробілами тощо.
mklement0

Перевага цього підходу перед @ vmpstr полягає в тому, що код виходу правильно відображає, чи всі пошукові терміни знайдені чи ні.
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, вам доведеться врятуватися так: \|для пошуку Данська, Норська та Свенської.

  2. Припускає, що один рядок має лише одну мову.

Покрокове керівництво: http://www.cyberciti.biz/faq/howto-use-grep-command-in-linux-unix/


Хіба це не зазнає невдачі, якби Данськ Норськ і Свенська опинилися на одному рядку?
vmpstr

Так, у такому випадку це не вдалося б. Я припустив, що мови відображаються по одній на рядок.
Damodharan R

Він також подав би файл, якби я мав лише Norsk, але в трьох різних рядках.
Бенджамін В.

6

Ви можете зробити це дуже легко за допомогою ack :

ack -l 'cats' | ack -xl 'dogs'
  • -l: повернути список файлів
  • -x: взяти файли зі STDIN (попередній пошук) і шукати лише ці файли

І ви можете просто продовжувати трубопроводи, поки не отримаєте потрібні файли.


Коли я пробую це, це говорить Unknown option: x. Чи існує певна версія ack, яка підтримує цей прапор x?
Хасан

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
у вашому реченні awk END ви, мабуть, хочете:, if (a && b && c) {exit 0} else {exit 1}або більш exit !(a && b && c)
стисло

ваше рубінове рішення не виглядає правильно. що друкуватиме лише абзаци, що містять усі пошукові слова. питання полягає в тому: чи містить файл (у цілому) всі слова, навіть якщо всі вони не містяться в одному абзаці.
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
Питання задає вираз, який повертає файли, що містять усі три терміни; це повертає рядки (замість імен файлів), що містять будь-який із трьох (замість усіх трьох).
Бенджамін В.

2

Це поєднання відповідей Глена Джекмена та Курумі, яке дозволяє довільну кількість регулярних виразів замість довільної кількості фіксованих слів або фіксованого набору регулярних виразів.

#!/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, ось функція bash:

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

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

--No-index здійснює пошук файлів у поточному каталозі, яким не керує Git. Отже, ця команда буде працювати в будь-якому каталозі, незалежно від того, є це сховищем git чи ні.


0

У мене була ця проблема сьогодні, і всі однокласні лайнери мені не вдались, оскільки файли містили пробіли в іменах.

Це те, що я придумав, що спрацювало:

grep -ril <WORD1> | sed 's/.*/"&"/' | xargs grep -il <WORD2>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.