Что я должен использовать, когда сокращение не сокращает это?

19

У меня есть файл, citiesкак это:

[1598] San Diego, US (inactive)
[4517] St Louis, US (inactive)
[6346] Orlando, US (inactive)

Я хочу вырезать названия городов, чтобы у меня было:

San Diego
St Louis
Orlando

Это лучшее, что я мог придумать:

cut -d ',' -f1 cities | cut -d ']' -f2

Но это все еще оставляет мне пробел перед именами. Есть ли cutподобная команда, которую я могу использовать, которая принимает разделители из нескольких символов, чтобы я мог вырезать ]?

Кит Сунде
источник
1
trполезно для удаления ненужных символов.
LawrenceC
Если вы попробуете код в ответах людей, вы увидите три разных результата. Это говорит о том, что ваш вопрос не был ясен на 100%. Означает ли "вырезать" удалить или выбрать? Хотите (inactive)статус или нет? Пожалуйста, предоставьте образец вывода.
Микель
@Mikel - Учитывая, что я использую, cutчтобы вырезать вещи, и вы можете увидеть цель неудачного примера, который я имею, это должно быть достаточно ясно в контексте. Я предоставлю образец, хотя, чтобы прояснить это далее. :)
Кит Сунде
Нет, не совсем. Я изменил одно предложение в вашем вопросе на «печатать только названия городов», потому что вы не поняли, что вы используете слово «вырезать». Правильно ли мое изменение?
Микель
1
@Kit Sunde: с примером вывода, это, конечно, понятно. Название мило. «Вырез» заставляет меня задуматься о том, что происходит, когда вы нажимаете Ctrl + X, поэтому я предложил изменить, но это ваш вопрос. Понижать голос было бы глупо, когда это просто несогласие.
Микель

Ответы:

15

Awk (также проверьте Awk Info ) прекрасно с таким вопросом. Пытаться:

awk -F'[],] *' '{print $2}' cities

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

Как только линия разделена, вы можете делать то, что вы хотите с результатом разделения. Здесь я решил распечатать второе поле только с print $2. Обратите внимание, что важно использовать одинарные кавычки вокруг инструкций awk, иначе $ 2 заменяется на оболочку.

asoundmove
источник
2
]это не угловая скобка. Угловые скобки есть <>. []это «квадратные скобки», или просто «скобки».
cjm
Я думаю, что вам нужно избежать этой закрывающей скобки, если только мне действительно не нужно читать мои регулярные выражения.
Кит Сунд
@cjm - Может быть, он немец: news.ycombinator.com/item?id=1181243 :)
Кит Сунде,
1
@ cjm, извините, я хотел сказать квадратную скобку, набрал слишком быстро. @Kit, я не немец. Вы не хотите экранировать внутреннюю закрывающую скобку (это бесполезно), но это должен быть первый символ в диапазоне.
asoundmove
12

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

cut -d ' ' -f2-

Вышеуказанное означает, что разделитель полей является пробелом, и мы хотим выбрать все поля, начиная со второго. Полная последовательность становится:

cut -d ',' -f1 cities | cut -d ' ' -f2-
Барун
источник
12

Для более сложного разбора вы должны использовать sed (1) :

sed -e 's/\[[0-9]\+\] \([^,]\+\),.*/\1/' cities

Или, используя -rдля упрощения регулярное выражение, как предлагает pepoluan :

sed -re 's/\[[0-9]+\] ([^,]+),.*/\1/' cities
Жулиано
источник
2
+1. Вы также можете использовать -r, чтобы избежать экранирования расширенных символов регулярных выражений, что значительно упрощает шаблон регулярных выражений
pepoluan
0

Я обычно использую Perl, когда все становится слишком сложно для sed и grep.

Есть несколько способов написать это на Perl. Например, вы могли бы предпочесть, чтобы он был быстрым, или вы могли бы предпочесть, чтобы он обрабатывал небольшие неожиданные проблемы при вводе (например, два пробела, где ожидалось одно).

Один очевидный способ (предполагается, что id является числовым, город - буквенным, статус - буквенным):

while (<>) {
    if (/^\[\d+\] (\w+(?: \w+)*), \w+ \(\w*\)$/) {
        my $city = $1;
        print "$city\n";
    }
}

Или медленнее, но более разрешительным (делает больше возврата):

while (<>) {
    if (/^.*\]\s+(.*),.*$/) {
        my $city = $1;
        print "$city\n";
    }
}

Или быстрее (поле останавливается при первом появлении закрывающей скобки):

while (<>) {
    if (/^\[[^]]*\] ([^,]*), \S+ \([^)]*\)$/) {
        my $city = $1;
        print "$city\n";
    }
}

Из командной строки, а не из скрипта, вы можете использовать -nопцию, которая в основном добавляет while (<>) { BLOCK }цикл:

perl -ne '/^\[[^]]*\] ([^,]*), \S+ \([^)]*\)$/ and print $1, "\n";' cities

или если вы хотите, чтобы использование было похоже на cut, вы можете использовать -Fопцию, которая похожа на -Fопцию awk , например:

perl -a -n -F'/[],]\s+/' -e 'print $F[1], "\n"' cities

Этот способ, очевидно, предполагает, что ни одно поле не будет содержать разделителей.

Mikel
источник