Сопоставьте две строки в одной строке с grep - PullRequest
191 голосов
/ 20 декабря 2010

Я пытаюсь использовать grep для сопоставления строк, которые содержат две разные строки. Я пробовал следующее, но это соответствует строки, которые содержат string1 или string2 , что не то, что я хочу.

grep 'string1\|string2' filename

Так как же сопоставить с grep только строки, содержащие обе строки ?

Ответы [ 20 ]

1 голос
/ 30 апреля 2019

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 и др.

Похожие:

Для операции ИЛИ см .:

1 голос
/ 28 апреля 2019

Не пытайтесь использовать для этого 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.

1 голос
/ 25 февраля 2018
grep -i -w 'string1\|string2' filename

Это работает для точного совпадения слов и совпадения слов без учета регистра, для этого используется -i

1 голос
/ 04 мая 2017
grep '(string1.*string2 | string2.*string1)' filename

получит строку со строкой1 и строкой2 в любом порядке

1 голос
/ 26 апреля 2017

Поместите строки, которые вы хотите найти в файл

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 
0 голосов
/ 10 июля 2019
grep ‘string1\|string2’ FILENAME 

GNU grep версия 3.1

0 голосов
/ 30 апреля 2019

ripgrep

Вот пример использования rg:

rg -N '(?P<p1>.*string1.*)(?P<p2>.*string2.*)' file.txt

Это один из самых быстрых инструментов для подрезки,поскольку он построен на основе движка регулярных выражений Rust , который использует конечные автоматы, SIMD и агрессивные буквальные оптимизации для очень быстрого поиска.

Используйте его, особенно когда вы работаете с большими данными.

См. Также запрос связанных функций на GH-875 .

.
0 голосов
/ 20 декабря 2010

Вы должны иметь grep, как это:

$ grep 'string1' file | grep 'string2'
0 голосов
/ 01 июля 2017

Я часто сталкиваюсь с той же проблемой, что и ваша, и я просто написал фрагмент сценария:

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 "$@"; 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, еслитебе нравится.

0 голосов
/ 24 октября 2013

для многострочного соответствия:

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"

нам просто нужно удалить символ новой строки, и это работает!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...