Як зробити grep для вмісту за зразком?


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///- substitute) будь-який символ ( .*) від початку рядка ( ^) до останнього входження послідовності :(двокрапки, після якої пробіл) пустим рядок ( 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:/)повертає true, якщо регулярний вираз /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
Awk би був більш ідіоматичноawk '/potato:/ {print $2}'
Бенджамін W.

Сценарії 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

Можна уявити Грепа як обмежений Сед, або Седа як узагальненого Грепа. У цьому випадку 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 або будь-який зовнішній інструмент. Ви можете зробити це за допомогою чистого башу.

Спробуйте це (крапки з комою є, щоб дозволити вам розмістити все в одному рядку):

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

Крім того, ви повинні використовувати, read -rякщо ви дуже конкретно не вимагаєте деякої досить прикрої застарілої поведінки до POSIX.
триплі

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.