Попробуйте это:
awk 'BEGIN {FS=","} {print $1, $2}' file | sort -k1,1 -nk2,2
Пояснение:
'BEGIN {FS=","} {print $1, $2}'
-установить разделитель полей на,
(пробел по умолчанию )и печатать только первое($1
)и второе($2
)поля; file
-вы файл (вместо этого вы можете использовать трубу:cat file | awk etc...
); 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
Это то, что вам нужно?
В 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/
В vim вам, вероятно, потребуется экранировать ()
. У меня есть что-то вроде этого:
:s/.* \(.\+.\+$\)/\1/
Он разбивается на строки, в которых нет хотя бы 2 пробелов.
То, что вы ищете::s/^\S*\s\S*\s\S*\s//
где ^ обозначает начало строки,
\s означает «белый -пробел» (пробел или табуляция)
а \S означает «нет -белого -пробела»
Это можно «сокращить» до::s/^\(\S*\s\)\{3\}//
что означает три вхождения «любого количества ни одного -белого -пробела», за которым следует один пробел
Это должно совпасть и удалить «Abbey Street E.2» и оставить «Buckfast Street».
Мы также можем использовать 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
Нужно поставить якорь. Поскольку вы хотите, чтобы последние два слова работали с конца,:$
соответствует концу строки, затем не -белый (один или несколько ), то есть [^ ][^ ]*
, затем один пробел и так далее. Сгруппируйте интересующую часть (, чтобы она начиналась после пробела ), замените полную строку частью, заключенной в квадратные скобки. Не знаю навскидку, если sed(1)
везде использует расширенные регулярные выражения, я использую это здесь:
sed -ne 's/^.* ([^ ][^ ]* [^ ][^ ]*)/\1/p'
Полностью не проверено, используйте на свой страх и риск.