sed: печатать только соответствующую группу

134

Я хочу получить два последних числа (одно целое, одно с плавающей запятой; за ним следует необязательный пробел) и распечатать только их.

Пример:

foo bar <foo> bla 1 2 3.4

Следует напечатать:

2 3.4

Пока у меня есть следующее:

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/replacement/p' 

даст мне

foo bar <foo> bla 1 replacement

Однако, если я попытаюсь заменить его на группу 1, будет напечатана вся строка.

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/\1/p' 

Как я могу распечатать только ту часть строки, которая соответствует регулярному выражению в группе?

Морт
источник

Ответы:

139

Сопоставьте всю строку, поэтому добавьте .*в начало вашего регулярного выражения. Это приводит к замене всей строки содержимым группы

echo "foo bar <foo> bla 1 2 3.4" |
 sed -n  's/.*\([0-9][0-9]*[\ \t][0-9.]*[ \t]*$\)/\1/p'
2 3.4
Iruvar
источник
38
Мне пришлось добавить параметр -rили --regexp-extended, иначе я получал invalid reference \1 on ошибку s 'command's RHS'.
Даниэль Соколовски
15
@DanielSokolowski Я думаю, вы получите эту ошибку, если будете использовать (и )вместо \(и \).
Даниэль Дарабос,
3
Также не забудьте добавить .*в конец регулярного выражения, если строка, которую вы хотите извлечь, не всегда находится в конце строки.
Teemu Leisti
3
Это не сработает для меня, потому что .*он жадный, а в sed нет не жадного.*?
sondra.kinsey
@DanielDarabos Просто упомяните это, (и )это не вызовет ошибки в ubuntu 16.04. Так что я думаю, что этот комментарий устарел.
Ли Хаонан
72

Grep - правильный инструмент для извлечения.

используя ваш пример и ваше регулярное выражение:

kent$  echo 'foo bar <foo> bla 1 2 3.4'|grep -o '[0-9][0-9]*[\ \t][0-9.]*[\ \t]*$'
2 3.4
Кент
источник
12
отлично подходит
jozxyqk
grep -o не переносит на системы, на которых работает msysgit, но sed делает.
cchamberlain
См. Вопрос, связанный с @jozxyqk, для ответа, в котором для решения этой проблемы с помощью grep используются упреждающие и ретроспективные методы.
Иоахим Брайтнер
Вы можете извлечь группу из шаблона с помощью конвейерных grep -oвызовов. stackoverflow.com/a/58314379/117471
Бруно Броноски
12

И в качестве еще одного варианта я бы выбрал awk!

echo "foo bar <foo> bla 1 2 3.4" | awk '{ print $(NF-1), $NF; }'

Это разделит ввод (здесь я использую STDIN, но ваш ввод легко может быть файлом) на пробелы, а затем распечатать предпоследнее поле, а затем последнее поле. $NF переменных содержат количество полей обнаружено после взрыва на пространствах.

Преимущество этого заключается в том, что не имеет значения, изменится ли то, что предшествует двум последним полям, до тех пор, пока вам нужны только два последних поля, оно будет продолжать работать.

chooban
источник
3

Команда вырезания предназначена именно для этой ситуации. Он будет «вырезать» любой разделитель, а затем вы можете указать, какие фрагменты следует выводить.

Например: echo "foo bar <foo> bla 1 2 3.4" | cut -d " " -f 6-7

Результатом будет вывод: 2 3.4

-d устанавливает разделитель

-f выбирает диапазон «полей» для вывода, в данном случае это фрагменты с 6-го по 7-й исходной строки. Вы также можете указать диапазон в виде списка, например 6,7.

carlin.scott
источник
Чтобы напечатать только определенные столбцы, выполните трубу доawk '{ print $2" "$6 }'
nurettin
@nurettin Я думаю, ваш комментарий мог быть предназначен для одного из ответов awk.
carlin.scott
Когда я посетил эту страницу, я попробовал cut и понял, что это ограничения, и решил вместо этого написать более обобщенную версию на awk в качестве комментария, чтобы улучшить качество этой публикации.
nurettin
1
Да, я думаю, это относится к другому ответу, связанному с awk. Команда вырезания для выполнения того, что вы написали:cut -d " " -f 2,6
carlin.scott
ах, я этого не знал, я думал, вы можете указать только диапазоны. Спасибо за это.
nurettin
2

Я согласен с @kent, что это хорошо подходит grep -o. Если вам нужно извлечь группу в шаблоне, вы можете сделать это с помощью второй команды grep.

# To extract \1 from /xx([0-9]+)yy/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'xx[0-9]+yy' | grep -Eo '[0-9]+'
123
4

# To extract \1 from /a([0-9]+)b/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'a[0-9]+b' | grep -Eo '[0-9]+'
678
9
Бруно Броноски
источник