Как сопоставить все символы до предпоследнего пробела в строке?

Попробуйте это:

awk 'BEGIN {FS=","} {print $1, $2}' file | sort -k1,1 -nk2,2

Пояснение:

  1. 'BEGIN {FS=","} {print $1, $2}'-установить разделитель полей на,(пробел по умолчанию )и печатать только первое($1)и второе($2)поля;
  2. file-вы файл (вместо этого вы можете использовать трубу:cat file | awk etc...);
  3. sort -k1,1 -nk2,2-сортировка по первому полю(-k1,1)и следующий шаг -сортировка по второму полю численно(-nk2,2)

Давайте усложним ваш пример (на самом деле ваши первые поля равны ). Предположим, у вас есть другой входной файл, подобный этому:

male,9,13,6.3152956461
female,12,12,5.4797699786
female,11,11,3.6421699174
male,9,14,4.5933365997
male,8,14,2.4181574607

Вывод будет:

male 8
male 9
male 9
female 11
female 12

Это то, что вам нужно?

2
17.02.2020, 21:24
5 ответов

В sed (мне проще тестировать в нем ), мы можем построить необходимое регулярное выражение.
[^ ]*должно соответствовать любому слову (, если нет знаков пунктуации ). Так:

$ a="Abbey Street E.2 Buckfast Street"
$ echo "$a" | sed 's/[^ ]*//'
 Street E.2 Buckfast Street

удалит первое слово. Обратите внимание, что в выводе оставлен пробел. Затем нам также нужно удалить пробел. И повторите то же самое три раза, чтобы удалить 3 начальных слова (и оставить последние два):

$ echo "$a" | sed 's/\([^ ]* \)\{3\}//'
Buckfast Street

Но в вашем описании вы указали:до предпоследнего пробела , это другое. Удаление трех слов из предложения из 6 слов оставит 3 слова, а не последние два .

Итак, нам нужно работать в обратном порядке, и, чтобы увидеть эффекты регулярного выражения, я зафиксирую каждый раздел и распечатаю его, разделив |==|.

Ваша основная идея для захвата слова состояла в том, чтобы использовать [^ ]*, и да, иногда (это могло сработать ). Использование -E, чтобы избежать \'s:

$ echo "$a" | sed -E 's/([^ ]*)(.*)/\1|==|\2/'
Abbey|==| Street E.2 Buckfast Street

Он фиксирует первое слово в первых скобках и «все остальные»(.*)во вторых скобках. Но если мы хотим обратить регулярное выражение:

$ echo "$a" | sed -E 's/(.*)([^ ]*)/\1|==|\2/'
Abbey Street E.2 Buckfast Street|==|

Здесь происходит то, что .*захватывает все, а следующая часть захватывает ноль символов (, что является допустимым результатом для*). Нам нужна некоторая привязка или разделитель , какой-то символ или точка, которые заставляют регулярное выражение соответствовать определенной точке. Мы можем использовать пробел в качестве разделителя и $ в качестве привязки, чтобы гарантировать, что выбранное слово на самом деле является последним строки:

$ echo "$a" | sed -E 's/(.* )([^ ]*)$/\1|==|\2/'
Abbey Street E.2 Buckfast |==|Street

Повторяя пробелы, мы сопоставляем последние два слова:

$ echo "$a" | sed -E 's/(.* )([^ ]* [^ ]*)$/\1|==|\2/'
Abbey Street E.2 |==|Buckfast Street

Теперь,выберите партию, которую вы хотите сохранить и/или стереть:

$ echo "$a" | sed -E 's/(.* )([^ ]* [^ ]*)$/\2/'
Buckfast Street

Конечно, на данный момент нет необходимости захватывать первую часть:

$ echo "$a" | sed -E 's/.* ([^ ]* [^ ]*)$/\1/'
Buckfast Street

Эквивалент этого ERE в BRE работает в vim:

:s/.* \([^ ]* [^ ]*\)$/\1/
2
28.04.2021, 23:23

В vim вам, вероятно, потребуется экранировать (). У меня есть что-то вроде этого:

:s/.* \(.\+.\+$\)/\1/

Он разбивается на строки, в которых нет хотя бы 2 пробелов.

0
28.04.2021, 23:23

То, что вы ищете:
:s/^\S*\s\S*\s\S*\s//
где ^ обозначает начало строки,
\s означает «белый -пробел» (пробел или табуляция)
а \S означает «нет -белого -пробела»

Это можно «сокращить» до:
:s/^\(\S*\s\)\{3\}//
что означает три вхождения «любого количества ни одного -белого -пробела», за которым следует один пробел

Это должно совпасть и удалить «Abbey Street E.2» и оставить «Buckfast Street».

0
28.04.2021, 23:23

Мы также можем использовать awk для вывода двух последних слов строки:

awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'

Примеры:

$ echo ""|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
$ echo "1"|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
1
$ echo "1 22"|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
1 22
$ echo "1 22 333"|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
22 333
$ echo "1 22 333 4444"|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
333 4444

В случае sed я бы использовал:

sed 's/^.*\s\([^ \t]\+\)\s\+\([^ \t]\+\)\s*$/\1 \2/g'

Пример:

$ echo " 1  22   3333  4444   "|sed 's/^.*\s\([^ \t]\+\)\s\+\([^ \t]\+\)\s*$/\1 \2/g'
3333 4444

Сложность увеличивается из-за необходимости правильной работы со строками, содержащими несколько пробелов в строке (или символов табуляции ), а также возможным дополнительным пробелом (с )в конце строки, вывод в данном случае :два разделенных одним пробелом слова. Однако в этом случае не покрываются строки только с одним словом или строки только с пробелами, они будут напечатаны без изменений. Мы можем позаботиться об этом, но это сделает команду sed еще более сложной, поэтому я ее здесь опускаю.


Обновлено.

В случае MacOS sed это выглядит так (Я исключил символы табуляции, чтобы упростить):

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

Пример:

$ echo " 1  22   3333  4444   "|sed 's/^.* \([^ ][^ ]*\)  *\([^ ][^ ]*\) *$/\1 \2/g'
3333 4444
0
28.04.2021, 23:23

Нужно поставить якорь. Поскольку вы хотите, чтобы последние два слова работали с конца,:$соответствует концу строки, затем не -белый (один или несколько ), то есть [^ ][^ ]*, затем один пробел и так далее. Сгруппируйте интересующую часть (, чтобы она начиналась после пробела ), замените полную строку частью, заключенной в квадратные скобки. Не знаю навскидку, если sed(1)везде использует расширенные регулярные выражения, я использую это здесь:

sed -ne 's/^.* ([^ ][^ ]* [^ ][^ ]*)/\1/p'

Полностью не проверено, используйте на свой страх и риск.

0
28.04.2021, 23:23

Теги

Похожие вопросы