Ответ 1
Вы можете использовать grep 'string1' filename | grep 'string2'
Или, grep 'string1.*string2\|string2.*string1' filename
Я пытаюсь использовать grep
для сопоставления строк, содержащих две разные строки. Я пробовал следующее, но это соответствует строкам, которые содержат строку string1 или, которая не то, что я хочу.
grep 'string1\|string2' filename
Итак, как мне сопоставить с grep
только строки, содержащие обе строки?
Вы можете использовать grep 'string1' filename | grep 'string2'
Или, grep 'string1.*string2\|string2.*string1' filename
Я думаю, что это то, что вы искали:
grep -E "string1|string2" filename
Я думаю, что ответы вроде этого:
grep 'string1.*string2\|string2.*string1' filename
соответствуют только случаю, когда оба присутствуют, а не один или другой или оба.
Просто укажите несколько опций -e
.
-e pattern, --regexp=pattern
Specify a pattern used during the search of the input: an input
line is selected if it matches any of the specified patterns.
This option is most useful when multiple -e options are used to
specify multiple patterns, or when a pattern begins with a dash
(`-').
Таким образом, команда становится:
grep -e "string1" -e "string2" filename
Примечание. Выше я привел руководство по версиям BSD, но выглядит как то же самое в Linux.
Поиск файлов, содержащих все слова в любом порядке:
grep -ril \'action\' | xargs grep -il \'model\' | xargs grep -il \'view_type\'
Первый grep запускает рекурсивный поиск (r
), игнорируя регистр (i
) и перечисляя (распечатывая) имя файлов, которые соответствуют (l
) для одного термина ('action'
с одинарными кавычками), встречающихся в любом месте файла.
Последующие greps ищут другие термины, сохраняя нечувствительность к регистру и выводя соответствующие файлы.
Окончательный список файлов, которые вы получите, будет содержать те, которые содержат эти термины, в любом порядке в любом месте файла.
Если у вас есть grep
с опцией -P
для ограниченного регулярного выражения perl
, вы можете использовать
grep -P '(?=.*string1)(?=.*string2)'
который имеет преимущество в работе с перекрывающимися строками. Это несколько более прямолинейно, используя perl
как grep
, потому что вы можете напрямую указать и логику:
perl -ne 'print if /string1/ && /string2/'
Ваш метод был почти хорошим, только без -w
grep -w 'string1\|string2' filename
Вы можете попробовать что-то вроде этого:
(pattern1.*pattern2|pattern2.*pattern1)
Оператор |
в регулярном выражении означает или. То есть либо строка1, либо строка2 будут совпадать. Вы можете сделать:
grep 'string1' filename | grep 'string2'
который будет передавать результаты первой команды во второй grep. Это должно дать вам только строки, которые соответствуют обоим.
И поскольку люди предложили perl и python, а также запутанные сценарии оболочки, вот простой подход awk:
awk '/string1/ && /string2/' filename
Посмотрев комментарии к принятому ответу: нет, это не делает многострочный; но тогда это также не то, о чем просил автор вопроса.
Найденные строки, которые начинаются с 6 пробелов и заканчиваются:
cat my_file.txt | grep
-e '^ .*(\.c$|\.cpp$|\.h$|\.log$|\.out$)' # .c or .cpp or .h or .log or .out
-e '^ .*[0-9]\{5,9\}$' # numers between 5 and 9 digist
> nolog.txt
Скажем, нам нужно найти количество нескольких слов в файле файла. Есть два способа сделать это
1) Используйте команду grep с шаблоном соответствия регулярных выражений
grep -c '\<\(DOG\|CAT\)\>' testfile
2) Используйте команду egrep
egrep -c 'DOG|CAT' testfile
С egrep вам не нужно беспокоиться о выражении и просто отделять слова от разделителя труб.
Поместите строки, которые вы хотите использовать grep, в файл
echo who > find.txt
echo Roger >> find.txt
echo [44][0-9]{9,} >> find.txt
Затем поиск с использованием -f
grep -f find.txt BIG_FILE_TO_SEARCH.txt
grep '(string1.*string2 | string2.*string1)' filename
получит строку с string1 и string2 в любом порядке
grep -i -w 'string1\|string2' filename
Это работает для точного совпадения слов и совпадения слов без учета регистра, для этого используется -i
Не пытайтесь использовать grep для этого, используйте вместо этого awk. Чтобы сопоставить 2 регулярных выражения R1 и R2 в grep, можно подумать, что это будет:
grep 'R1.*R2|R2.*R1'
в то время как в awk это будет:
awk '/R1/ && /R2/'
но что, если R2
перекрывается или является подмножеством R1
? Эта команда grep просто не будет работать, в то время как команда awk будет работать. Допустим, вы хотите найти строки, которые содержат the
и heat
:
$ echo 'theatre' | grep 'the.*heat|heat.*the'
$ echo 'theatre' | awk '/the/ && /heat/'
theatre
Для этого вам нужно использовать 2 greps и трубу:
$ echo 'theatre' | grep 'the' | grep 'heat'
theatre
и, конечно, если вы действительно требовали, чтобы они были отдельными, вы всегда можете написать в awk то же регулярное выражение, которое вы использовали в grep, и есть альтернативные решения awk, которые не предполагают повторение регулярных выражений во всех возможных последовательностях.
Если оставить в стороне, что делать, если вы хотите расширить свое решение для соответствия 3 регулярным выражениям R1, R2 и R3. В grep это был бы один из этих неудачных вариантов:
grep 'R1.*R2.*R3|R1.*R3.*R2|R2.*R1.*R3|R2.*R3.*R1|R3.*R1.*R2|R3.*R2.*R1' file
grep R1 file | grep R2 | grep R3
в то время как в awk это будет кратким, очевидным, простым, эффективным:
awk '/R1/ && /R2/ && /R3/'
Теперь, что если вы действительно хотите сопоставить литеральные строки S1 и S2 вместо регулярных выражений R1 и R2? Вы просто не можете сделать это за один вызов grep, вы должны либо написать код, чтобы экранировать все метасхемы RE, прежде чем вызывать grep:
S1=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R1')
S2=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R2')
grep 'S1.*S2|S2.*S1'
или снова используйте 2 greps и трубу:
grep -F 'S1' file | grep -F 'S2'
что опять-таки плохой выбор, тогда как с awk вы просто используете строковый оператор вместо оператора регулярного выражения:
awk 'index($0,S1) && index($0.S2)'
А что если вы хотите сопоставить 2 регулярных выражения в абзаце, а не в строке? Не может быть сделано в grep, тривиально в awk:
awk -v RS='' '/R1/ && /R2/'
Как насчет всего файла? Опять же, это не может быть сделано в grep и тривиально в awk (на этот раз я использую GNU awk для multi-char RS для краткости, но это не намного больше кода в любом awk, или вы можете выбрать контрольный char, который, как вы знаете, не будет быть на входе для RS, чтобы сделать то же самое):
awk -v RS='^$' '/R1/ && /R2/'
Итак, если вы хотите найти несколько регулярных выражений или строк в строке, абзаце или файле, не используйте grep, используйте awk.
git grep
Вот синтаксис с использованием git grep
с несколькими шаблонами:
git grep --all-match --no-index -l -e string1 -e string2 -e string3 file
Вы также можете комбинировать шаблоны с логическими выражениями, такими как --and
, --or
и --not
.
Обратитесь к представителю man git-Grep
за помощью.
--all-match
При задании нескольких выражений-шаблонов этот флаг указывается для ограничения соответствия файлам, строки которых соответствуют всем из них.
--no-index
Поиск файлов в текущем каталоге, который не управляется Git.
-l
/--Files-with-matches
/--name-only
Показывать только имена файлов.
-e
Следующий параметр - это шаблон. По умолчанию используется базовое регулярное выражение.
Другие параметры для рассмотрения:
--threads
Количество используемых рабочих потоков grep.
-q
/--quiet
/--silent
Не выводить совпавшие строки; выйти со статусом 0, когда есть совпадение.
Чтобы изменить тип шаблона, вы также можете использовать -G
/--basic-regexp
(по умолчанию), -F
/--Fixed-strings
, -e
/--extended-regexp
, -P
/--Perl-regexp
, -F file
и другие.
Связанные с:
Для операции ИЛИ см.:
У вас должно быть grep
следующее:
$ grep 'string1' file | grep 'string2'
для многострочного соответствия:
echo -e "test1\ntest2\ntest3" |tr -d '\n' |grep "test1.*test3"
или
echo -e "test1\ntest5\ntest3" >tst.txt
cat tst.txt |tr -d '\n' |grep "test1.*test3\|test3.*test1"
нам просто нужно удалить символ новой строки, и он работает!
Я часто сталкиваюсь с той же проблемой, что и ваша, и я просто написал фрагмент script:
function m() { # m means 'multi pattern grep'
function _usage() {
echo "usage: COMMAND [-inH] -p<pattern1> -p<pattern2> <filename>"
echo "-i : ignore case"
echo "-n : show line number"
echo "-H : show filename"
echo "-h : show header"
echo "-p : specify pattern"
}
declare -a patterns
# it is important to declare OPTIND as local
local ignorecase_flag filename linum header_flag colon result OPTIND
while getopts "iHhnp:" opt; do
case $opt in
i)
ignorecase_flag=true ;;
H)
filename="FILENAME," ;;
n)
linum="NR," ;;
p)
patterns+=( "$OPTARG" ) ;;
h)
header_flag=true ;;
\?)
_usage
return ;;
esac
done
if [[ -n $filename || -n $linum ]]; then
colon="\":\","
fi
shift $(( $OPTIND - 1 ))
if [[ $ignorecase_flag == true ]]; then
for s in "${patterns[@]}"; do
result+=" && s~/${s,,}/"
done
result=${result# && }
result="{s=tolower(\$0)} $result"
else
for s in "${patterns[@]}"; do
result="$result && /$s/"
done
result=${result# && }
fi
result+=" { print "$filename$linum$colon"\$0 }"
if [[ ! -t 0 ]]; then # pipe case
cat - | awk "${result}"
else
for f in "[email protected]"; do
[[ $header_flag == true ]] && echo "########## $f ##########"
awk "${result}" $f
done
fi
}
Использование:
echo "a b c" | m -p A
echo "a b c" | m -i -p A # a b c
Вы можете поместить его в .bashrc, если хотите.
grep ‘string1\|string2 FILENAME
GNU grep версия 3.1
ripgrep
Вот пример использования rg
:
rg -N '(?P<p1>.*string1.*)(?P<p2>.*string2.*)' file.txt
Это один из самых быстрых инструментов поиска, поскольку он построен на основе движка регулярных выражений Rust, который использует конечные автоматы, SIMD и агрессивные буквальные оптимизации, чтобы сделать поиск очень быстрым.
Используйте его, особенно когда вы работаете с большими данными.
См. Также запрос связанных функций на GH-875.