Как найти содержимое после шаблона?


81

Для файла, например:

potato: 1234
apple: 5678
potato: 5432
grape: 4567
banana: 5432
sushi: 56789

Я хотел бы использовать grep для всех строк, которые начинаются с, potato:но только для следующих чисел potato:. Итак, в приведенном выше примере результат будет следующим:

1234
5432

Как я могу это сделать?

Ответы:


113
grep 'potato:' file.txt | sed 's/^.*: //'

grepищет любую строку, содержащую строку potato:, затем для каждой из этих строк sedзаменяет ( s///- заменяет) любой символ ( .*) от начала строки ( ^) до последнего вхождения последовательности :(двоеточие, за которым следует пробел) пустым строка ( s/...//- замените первую часть второй частью, которая пуста).

или же

grep 'potato:' file.txt | cut -d\   -f2

Для каждой строки , которая содержит potato:, cutразделит линию на несколько полей , разделенных пробелами ( -d\- d= разделитель \= спасся пробел, что - то вроде -d" "бы и работал) и напечатать второе поле каждой такой линии ( -f2).

или же

grep 'potato:' file.txt | awk '{print $2}'

Для каждой строки , которая содержит potato:, awkбудет печатать второе поле ( print $2) , который ограничен по умолчанию пробелов.

или же

grep 'potato:' file.txt | perl -e 'for(<>){s/^.*: //;print}'

Все содержащиеся строки potato:отправляются в сценарий inline ( -e) Perl, который берет все строки из stdin, затем для каждой из этих строк выполняет ту же замену, что и в первом примере выше, а затем печатает ее.

или же

awk '{if(/potato:/) print $2}' < file.txt

Файл отправляется через stdin( < file.txtотправляет содержимое файла через stdinкоманду слева) в awkсценарий, который для каждой содержащейся строки potato:( if(/potato:/)возвращает истину, если регулярное выражение /potato:/соответствует текущей строке) печатает второе поле, как описано выше.

или же

perl -e 'for(<>){/potato:/ && s/^.*: // && print}' < file.txt

Файл отправляется через stdin( < file.txtсм. Выше) в сценарий Perl, который работает аналогично приведенному выше, но на этот раз он также проверяет, что каждая строка содержит строку potato:( /potato:/это регулярное выражение, которое соответствует, если текущая строка содержит potato:, и, если он выполняет ( &&), затем применяет регулярное выражение, описанное выше, и печатает результат).


3
Нет необходимости в двух процессах и трубе. Я бы пошел за awk '$1 ~ /potato/ { print $2 }' file.txt.
musiphil

2
Более идиоматично было бы awkawk '/potato:/ {print $2}'
Бенджамин В.

Скрипты Perl могли бы извлечь выгоду изperl -pe
tripleee

60

Или используйте утверждения регулярного выражения: grep -oP '(?<=potato: ).*' file.txt


4
Я попробовал несколько однострочных ответов из принятого выше ответа, но мне кажется, что этот ответ более точно решает вопрос.
Jake88

3
Некоторые пояснения: Параметр -oозначает печать только соответствующей части строки. Принимая во внимание , -Pделает вывод Perl - совместимый с регулярным выражением, которое случается быть положительным просмотром назад регулярное выражение (?<=string).
Serge Stroobandt

9
sed -n 's/^potato:[[:space:]]*//p' file.txt

Можно думать о Grep как о ограниченном Sed или о Sed как о обобщенном Grep. В данном случае Sed - это хороший и легкий инструмент, который делает то, что вы хотите, хотя, конечно, есть и несколько других разумных способов сделать это.


2

Это будет печатать все после каждого совпадения только в той же строке:

perl -lne 'print $1 if /^potato:\s*(.*)/' file.txt

Это будет делать то же самое, за исключением того, что он также распечатает все последующие строки:

perl -lne 'if ($found){print} elsif (/^potato:\s*(.*)/){print $1; $found++}' file.txt

Используются следующие параметры командной строки:

  • -n цикл вокруг каждой строки входного файла
  • -l удаляет символы новой строки перед обработкой и добавляет их после
  • -e выполнить код Perl

2
grep -Po 'potato:\s\K.*' file

-P использовать регулярное выражение Perl

-o выводить только совпадение

\s чтобы соответствовать пробелу после potato:

\K пропустить матч

.* чтобы соответствовать остальной части строки (строк)


1

Вы можете использовать grep, как указано в других ответах. Но вам не нужны grep, awk, sed, perl, cut или какие-либо внешние инструменты. Вы можете сделать это с помощью чистого bash.

Попробуйте это (точки с запятой используются, чтобы вы могли поместить все это в одну строку):

$ while read line;
  do
    if [[ "${line%%:\ *}" == "potato" ]];
    then
      echo ${line##*:\ };
    fi;
  done< file.txt

## указывает bash удалить самое длинное совпадение ":" в строке $ спереди.

$ while read line; do echo ${line##*:\ }; done< file.txt
1234
5678
5432
4567
5432
56789

или если вам нужен ключ, а не значение, %% указывает bash удалить самое длинное совпадение ":" в строке $ с конца.

$ while read line; do echo ${line%%:\ *}; done< file.txt
potato
apple
potato
grape
banana
sushi

Подстрока для разделения - ": \", потому что пробел должен быть экранирован обратной косой чертой.

Вы можете найти больше подобных в проекте документации по Linux .


while readочень медленно; использование внешней утилиты на самом деле будет намного быстрее, если вы выберете одну с буферизованным вводом-выводом (то есть практически любую из упомянутых в этом ответе и многих других).
Tripleee

Кроме того, вы должны использовать, read -rесли только вы особо не требуете некоторого довольно неприятного устаревшего поведения до POSIX.
Tripleee

0

Современный BASH поддерживает регулярные выражения:

while read -r line; do
  if [[ $line =~ ^potato:\ ([0-9]+) ]]; then
    echo "${BASH_REMATCH[1]}"
  fi
done
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.