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
vmpstr
источник
Умное решение; Следует отметить одну вещь (вообще говоря; не относящуюся к тому, о чем просил OP), что общий код выхода будет равен 0 даже в случае (концептуального) сбоя. Таким образом, если вас интересует определение неудачи или успеха, вам нужно либо проверить, пуст ли вывод stdout, либо вместо этого использовать подход @ EddSteel.
mklement0
@mklement: в Bash PIPESTATUSмассив содержит значения выхода членов конвейера.
Деннис Уильямсон
@DennisWilliamson Это хорошо, спасибо. Другой вариант - включить pipefailопцию оболочки (временно):shopt -so pipefail
mklement0 03
4
Возможно, вы захотите использовать grep -Zи, xargs -0если ваши имена файлов могут содержать пробелы.
Бен Челленор
1
Это может вызвать ошибку «Список аргументов слишком длинный», если у вас много файлов.
AnnanFay
23

Еще один способ с использованием только bash и grep:

Для одного файла test.txt:

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

Напечатает, test.txtесли файл содержит все три (в любой комбинации). Первые два grep ничего не печатают ( -q), а последний выводит файл только в том случае, если два других прошли.

Если вы хотите сделать это для каждого файла в каталоге:

   для f в *; do grep -q Dansk $ f && grep -q Norsk $ f && grep -l Svenska $ f; сделанный
Эдд Стил
источник
но тогда нет необходимости запускать grep 3 раза.
kurumi
1
Я знаю, что вы можете комбинировать шаблоны с -e, но я не видел способа создать соединение только в grep.
Эдд Стил,
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, вам придется избегать конвейера следующим образом: \|искать Dansk, Norsk и Svenska.

  2. Предполагает, что в одной строке только один язык.

Пошаговое руководство: http://www.cyberciti.biz/faq/howto-use-grep-command-in-linux-unix/

Damodharan R
источник
Разве это не сработает, если Данск Норск и Свенска окажутся на одной линии?
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».
Крис Варт
3

Просто:

grep 'word1\|word2\|word3' *

см. этот пост для получения дополнительной информации

Моше Бири
источник
Я бы добавил -lфлаг, но кроме этого, этот ответ кажется мне наиболее простым, если я чего-то не упускаю.
xdhmoore
Да, это также более эффективно, поскольку вы не обрабатываете все данные в нескольких
каналах
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

Расширяя ответ @kurumi awk, вот функция 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>
Джусти
источник