мы можем напечатать последнее слово каждой строки в linux, используя команду sed?

9

Предположим, если есть файл, состоящий из следующих строк, если они

12345 567 7878 66

   er3 t45t y6y46y 


 4y6 y656y y5y

   46y6 65y7 y66uyuy

 yy46y6y

Вывод должен выглядеть так:

66

y6y46y

y5y

y66uyuyy

y46y6y

Я пробовал sed 's/.* //g'имя файла команды и несколько других sedкоманд, но это не работает.

Могу ли я узнать, какая именно sedкоманда?

Раджив Нукала
источник
Это необходимо использовать sed?
coffeMug

Ответы:

8
awk '{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//'

Это все равно будет печатать пустую строку для каждой пустой строки. Чтобы этого избежать:

awk 'NF{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//;/./!d'
Стефан Шазелас
источник
Одно альтернативное выражение: sed -n 's/.*[[:blank:]]\+\([^[:blank:]]\+\)[[:blank:]]*$/\1/p'.
Джимми
@jimmij - это не работает, если последняя непустая последовательность также является первой и перед ней нет пробелов. Кроме того, вы могли бы также просто сделать .*в хвосте, вероятно - вы исключаете все, кроме конечных пробелов в любом случае W / .*[^[:blank:]].
mikeserv
4

Ты можешь попробовать :

  • sed 's/.* //'
  • awk '{print $NF}'
Уриэль
источник
4

Ты почти там. Просто укажите последнее слово:

sed 's/^.* \([^ ][^ ]*\)/\1/g'

Что оно делает:

  1. «^. *» удаляет все в начале строки и любые пробелы.
  2. '\ (...) \' соответствует шаблону и возвращает его как \ 1.
  3. «[^]» соответствует чему-либо без пробела.

(Отредактировано, чтобы добавить лучшее решение. Спасибо, Хильдред!)

Непрерывный тон
источник
1
Вот более короткое выражение: sed -r 's/.* ([^ ]+)/\1/g'если допускаются расширенные регулярные выражения, как правило, так.
Мкалков
Более короткая версия, использующая замену того, что вы не хотите сохранять, а не того, что вы хотите сохранить:sed 's/.* //'
Уриэль
2

Вы можете использовать некоторый адекватный шаблон grepвместо sed, например:

grep -o "[a-Z0-9]*$"

В этом примере [...]содержит диапазоны символов, которые считаются подходящими для «слова» (в этом случае могут быть добавлены буквенно-цифровые символы, другие символы, некоторые из которых должны быть экранированы).

Dalker
источник
2
Это предполагает, что в конце строки нет пробела. a-Zпоскольку диапазон не имеет особого смысла, даже в ASCII-локалях. Обратите внимание, что -oэто расширение GNU.
Стефан Шазелас
0

Если вы квалифицируете слово для обозначения любой последовательности из 1 или более непустых символов, тогда ответ определенно да, и это тоже очень просто сделать. Это потому, что [[:blank:]]*и [^[:blank:]]*являются логическими дополнениями и - при условии, что все символы в строке полны - [[:blank:]]*U [^[:blank:]]*может описать любую возможную строку почти так же, как это .*делает.

Если в строке существует неполный символ или иным образом недопустимая последовательность байтов, то ни один из них не может успешно описать ее с начала до конца - что иногда может происходить при интерпретации строки в неправильной кодировке. Чтобы обеспечить полный символ на байт в любой строке, локаль C может быть принудительно настроена следующим образом:

LC_ALL=C sed ...

... что позволит избежать любых проблем, описывающих строку от головы до хвоста с помощью всеобъемлющего шаблона, такого как .*или([ ]*[^ ]*)*

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

BRE:

sed 's/\(\([^[:blank:]]*\)[[:blank:]]*\)*/\2/'

ERE:

sed -E 's/(([^[:blank:]]*)[[:blank:]]*)*/\2/'

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

Сопоставив все это, волшебство происходит при замене - ссылки возвращаются группами \1и \2являются последними вхождениями каждого. Таким образом, когда замена сделана, вся строка заменяется только последним вхождением в строке, состоящей из нуля или более, не пустых символов - или подгруппы \2.

Конечно, это работает для любой возможной строки - даже пустой - что означает, что обе формы будут печатать символы новой строки для строк, которые содержат только пустые символы или вообще не содержат символов. Чтобы справиться с этим, есть пара вещей, которые вы можете сделать, но сначала давайте немного упростим ввод класса символов:

b='[:blank:]'

Теперь для печати, только если строка содержит один или несколько непустых символов, вы можете сделать:

BRE:

sed -n "s/\(\([^$b]*\)[$b]*\)*/\2/;/./p"

ERE:

sed -En "/[^$b]/s/(([^$b]*)[$b]*)*/\2/p"
  1. Случай BRE - замена всегда выполняется, и печатаются только пробелы с хотя бы одним оставшимся символом.
  2. ERE case - попытка замещения предпринимается только в пространстве шаблона, содержащем хотя бы один непустой символ.

Любая форма будет работать с любым методом - до тех пор, пока синтаксис правильный.

-nПереключатель отключает автоматический печати из шаблона, и pфлаг в s///ubstitution или /адресных /команд выдает его результаты только в случае успеха.

Эту же логику можно применять для получения любого {num}вхождения, например:

BRE:

sed -n "s/\([$b]*\([^$b]\{1,\}\)\)\{num\}.*/\2/p"

ERE:

sed -En "s/([$b]*([^$b]+)){num}.*/\2/p"

... где numв обоих регулярных выражениях можно заменить число, чтобы вывести только {num}указанное вхождение последовательности непустых символов. Здесь используется немного другая форма, чтобы гарантировать, что счетчик не смещен для начального пробела в строке.

Обратите внимание , что -Eпереключатель ERE к sedподдерживаются как в BSD и GNU версии, хотя это не является еще POSIX стандартного синтаксиса.

mikeserv
источник
Хорошие объяснения, хороший хак, но обратите внимание, что он не будет работать с традиционными sed реализациями (такими как Solaris / usr / bin / sed) и будет более дорогим, чем более простой подход (исчерпывает память с входными строками длиной более 25 символов с например, sed_su3из инструмента семейной реликвии). Поэтому, хотя мне и нравится ответ, я бы не рекомендовал такой подход.
Стефан Шазелас
Похоже, что не работает во FreeBSD.
Стефан Шазелас
@ StéphaneChazelas - да, производительность действительно ужасна для такой вещи, но она может быть очень эффективной для выбора пронумерованных событий. И для случая конца строки s/.* \([^[:blank:]]\{1,\}\).*/\1/гораздо лучше, но это сложнее, когда задействовано несколько строк. Однако на днях я обнаружил, что 's/\(\n\)*/\1/g;s/\n\(\n.*\)*/&&/[num];s///[samenum]могу довольно эффективно это поддержать. Во всяком случае, до тех пор, пока в логике нет явной ошибки, я счастлив - я просто думал, что, должно быть, что-то упустил.
mikeserv
@ StéphaneChazelas - о, а насчет старших sed- это немного странно - это должно звучать в соответствии со стандартом. xrat говорит ... Стандартные разработчики рассматривали общее историческое поведение, которое поддерживалось "\n*", но не поддерживалось , "\n\{min,max\}", "\(...\)*"или "\(...\)\{min,max\}"как
mikeserv
@ StéphaneChazelas - И стандарт говорит ... Если подвыражение, на которое ссылается обратная ссылка, совпадает с более чем одной строкой из-за звездочки ( '*' )или выражения интервала (см. Пункт (5)), обратная ссылка должна совпадать с последней (самой правой) ) из этих строк. Я почти уверен, что я проверял это / minisedхотя - конечно, я проверял что-то странное с / minisedв другой день, так или иначе.
mikeserv
-1

Да. Следующая команда sed сначала удаляет все завершающие пробелы ( s/ *$//), а затем все, вплоть до последнего пробела ( s/.* //). Вероятно, стоит заменить буквальный пробел [[:blank:]]на, чтобы захватывать вкладки и другие символы, похожие на пробелы.

$ echo "  aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  cc  " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "cc" | sed -e 's/ *$//' -e 's/.* //'
cc
mkalkov
источник