Повертайте лише частину рядка після відповідного шаблону


109

Таким чином, відкриття файлу з, catа потім використання grepдля отримання відповідних рядків отримує мене поки що, коли я працюю з конкретним набором журналів, з яким я маю справу. Потрібен спосіб зіставлення ліній за шаблоном, але лише повернення частини рядка після відповідності. Частина до і після матчу постійно змінюватиметься. Я грав з використанням sedабо awk, але не зміг зрозуміти, як відфільтрувати рядок, щоб видалити частину перед матчем, або просто повернути частину після матчу, або буде працювати. Це приклад рядка, який мені потрібно відфільтрувати:

2011-11-07T05:37:43-08:00 <0.4> isi-udb5-ash4-1(id1) /boot/kernel.amd64/kernel: [gmp_info.c:1758](pid 40370="kt: gmp-drive-updat")(tid=100872) new group: <15,1773>: { 1:0-25,27-34,37-38, 2:0-33,35-36, 3:0-35, 4:0-9,11-14,16-32,34-38, 5:0-35, 6:0-15,17-36, 7:0-16,18-36, 8:0-14,16-32,34-36, 9:0-10,12-36, 10-11:0-35, 12:0-5,7-30,32-35, 13-19:0-35, 20:0,2-35, down: 8:15, soft_failed: 1:27, 8:15, stalled: 12:6,31, 20:1 }

Мені потрібна порція - це все, після того, як "застопорився".

Передумовою цього є те, що я можу дізнатися, як часто щось зупиняється:

cat messages | grep stalled | wc -l

Що мені потрібно зробити - це дізнатися, скільки разів певний вузол застопорився (вказується частиною перед кожною кишкою після "затримки"). Якщо я просто натискаюся на це (тобто 20 :), він може повернути лінії, які мають м'які збої, але немає кіосків, що мені не допомагає. Мені потрібно фільтрувати лише застряглий ділянку, щоб потім я міг простукати за певний вузол із тих, що затрималися.

Для всіх намірів і цілей це система freebsd зі стандартними утилітами GNU, але я не можу встановити нічого додаткового, щоб допомогти.


@Gilles, як не дивно, як це не з’явилося під час пошуку, хоча я не використовував назву, з якою я врешті-решт пішов ... але він не відображався на екрані під моєю назвою. У будь-якому випадку, це може відвести мене там, де я хочу, хоча мені потрібен цілий рядок після матчу, а не перше слово - але це може не змінити багато змін.
MaQleod

Його назва засмоктується. Я вкрав ваше, що дуже приємно. Візьміть sedрозчин і не лікуйте пробіл спеціально.
Жиль

@ Gilles, це я не зовсім впевнений, як це зробити. Я все ще вчу sed.
MaQleod


1
@ shaa0601 Я не розумію вашого питання, особливо важко дотримуватися в коментарі без форматування. Задайте нове, самостійне питання.
Жиль

Відповіді:


141

Канонічним інструментом для цього було б sed.

sed -n -e 's/^.*stalled: //p'

Детальне пояснення:

  • -n означає не друкувати нічого за замовчуванням.
  • -e слідує команда sed.
  • s - команда заміни шаблону.
  • Регулярний вираз ^.*stalled:відповідає шаблону, який ви шукаєте, плюс будь-який попередній текст ( .*маючи на увазі будь-який текст, з початкового ^сказати, що збіг починається на початку рядка). Зауважте, що якщо stalled:трапляється кілька разів на лінії, це буде відповідати останньому явищу.
  • Збіг, тобто все в рядку до stalled:, замінюється порожнім рядком (тобто видаляється).
  • Останній pозначає надрукувати трансформований рядок.

Якщо ви хочете зберегти відповідну частину, використовуйте зворотний зв'язок: \1у замінній частині вказується, що знаходиться в межах групи \(…\)в шаблоні. Тут ви можете написати stalled:ще раз у замінній частині; ця функція корисна, коли шаблон, який ви шукаєте, більш загальний, ніж простий рядок.

sed -n -e 's/^.*\(stalled: \)/\1/p'

Іноді після матчу ви захочете видалити частину рядка. Ви можете включити його в поєдинок, включивши .*$в кінці шаблону (будь-який текст з .*наступним кінцем рядка $). Якщо ви не помістите цю частину в групу, на яку ви посилаєтесь в тексті заміни, кінець рядка не буде у висновку.

Як додаткова ілюстрація груп та зворотних посилань, ця команда замінює частину перед матчем та частину після матчу.

sed -n -e 's/^\(.*\)\(stalled: \)\(.*\)$/\3\2\1/p'

Я спробував перші два приклади, і він, здається, звисає. Я не отримую повідомлення про помилку, не отримую нове підказку, просто нічого.
MaQleod

2
@MaQleod О, він чекає на вхід на стандартному вході, який тут є терміналом, оскільки ви його не перенаправили. Тут ви зробите переспрямування вводу sed … <messages, оскільки ви хочете обробити дані з файлу. Для того, щоб впливати на дані , отримані з допомогою іншої команди, ви б використовувати трубу: somecommand | sed ….
Жиль

1
Так, затемнення доби в кінці дня. команда працює відмінно, дякую.
MaQleod

1
Найкраще пояснення седа, яке я бачив досі - дякую!
Джон Уодсворт

1
@ungalcrys Коротша версія чого? Це не еквівалентно жодній із команд моєї відповіді. Я рекомендую писати його так, sed 's/^.*stalled//'як -rце характерно для Linux і не працює в інших системах, таких як macOS, і тут ви не отримуєте жодної користі від цього.
Жиль

72

Інший канонічний інструмент, який ви вже використовуєте grep:

Наприклад:

grep -o 'stalled.*'

Має такий же результат, як і другий варіант Жиля:

sed -n -e 's/^.*\(stalled: \)/\1/p'

-oПрапор повертає --only-matchingчастину виразу, тому не вся лінія, - звичайно - зазвичай робиться Grep.

Щоб видалити "staled:" з виводу, ми можемо використовувати третій канонічний інструмент, вирізати:

grep -o 'stalled.*' | cut -f2- -d:

cutКоманда використовує роздільники :і друкує поле 2 до кінця. Це питання переваги, звичайно, але cutсинтаксис, який я знаходжу, дуже легко запам’ятати.


1
Дякуємо, що згадали про -oваріант! Я хотів би зазначити, що grepне визнає \nнову лінію, тому ваш перший приклад відповідає лише першому nсимволу. Наприклад, echo "Hello Anne" | grep -o 'A[^\n]*'повертає рядок A. Однак echo "Hello Anne" | grep -o 'A.*'повертає очікуване Anne, оскільки .відповідає будь-якому символу, окрім нового рядка.
adamlamar

1
Зауважте, що лапки навколо cutроздільника -d':'видалено @poige. Мені легше запам’ятати цитатами, наприклад, з -d' 'або -d';'.
Anne van Rossum

Згідно з вашим висновком, легше запам'ятати і використання лапок -f 2. Серйозно, чому б і ні?
poige

Тому що роздільник, як напів-двокрапка, ;а не двокрапка, :буде трактуватися по-різному, якщо не цитується. Звичайно, це логічна поведінка, але все ж я люблю покладатися на м’язову пам’ять. Мені не подобається цитувати роздільник один раз, але не інший раз. Просто особисті переваги, як я вже говорив раніше: легше запам’ятати.
Енн ван Россум

період , який є частиною .*потрібно, працює добре для мене: cat filename | grep 'Return only this line xyz text' | grep -o 'xyz.*' повертаєтьсяxyz text
RON

4

Я раніше ifconfig | grep eth0 | cut -f3- -d:брав це

    [root@MyPC ~]# ifconfig
    eth0  Link encap:Ethernet  HWaddr AC:B4:CA:DD:E6:F8
          inet addr:192.168.0.2  Bcast:192.168.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:78998810244 errors:1 dropped:0 overruns:0 frame:1
          TX packets:20113430261 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:110947036025418 (100.9 TiB)  TX bytes:15010653222322 (13.6 TiB)

і зробити так, щоб це виглядало так

    [root@MyPC ~]# ifconfig | grep eth0 | cut -f3- -d:
    C4:7A:4D:F6:B8

2
Чи відповідає це на питання?
Стівен Рауч

1
Ви можете використовувати cat /sys/class/net/*/address, не вимагаючи розбору.
Енн ван Россум

1

Ще один канонічний інструмент, який ви вважаєте, awkможе бути використаний у наступному рядку:

awk -F"stalled" '/stalled/{print $2}' messages

Детальне пояснення:

  • -Fвизначає роздільник для лінії, тобто "застопорився". Усе, перш ніж роздільник буде вирішено, $1і все після $2.
  • /reg-ex/ Пошуки відповідного регулярного виразу, в даному випадку "застопорилися".
  • {print $<n>}- друкує n стовпців. Оскільки ваш роздільник визначений як застопорений, все після зупинки вважається другим стовпцем.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.