Czego powinienem używać, gdy cięcie nie tnie?

19

Mam taki plik cities:

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

Chcę wyciąć nazwy miast, aby mieć:

San Diego
St Louis
Orlando

To najlepsze, co mogłem wymyślić:

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

Ale to wciąż pozostawia mi miejsce przed imionami. Czy cutmogę użyć polecenia podobnego, które akceptuje ograniczniki kilku znaków, dzięki czemu mogę kontynuować ]?

Kit Sunde
źródło
1
trjest przydatny do usuwania znaków, których nie chcesz.
LawrenceC
Jeśli wypróbujesz kod w odpowiedziach ludzi, zobaczysz trzy różne wyniki. To sugeruje, że twoje pytanie nie było w 100% jasne. Czy „wyciąć” oznacza usunąć lub wybrać? Chcesz (inactive)status, czy nie? Podaj przykładowe dane wyjściowe.
Mikel
@Mikel - Biorąc pod uwagę, że używam cutdo wycinania rzeczy i widać intencję nieudanego przykładu, który mam, powinien być dość jasny w kontekście. Podam próbkę, aby wyjaśnić ją dalej. :)
Kit Sunde,
Nie, nie bardzo. Zmieniłem jedno zdanie w twoim pytaniu, aby „drukować tylko nazwy miast”, ponieważ to, że użyłeś słowa „wytnij”, było dla mnie niejasne. Czy moja zmiana jest poprawna?
Mikel
1
@Kit Sunde: Z przykładowym wyjściem jest z pewnością zrozumiałe. Tytuł jest słodki. „cut out” sprawia, że ​​myślę o tym, co się dzieje po naciśnięciu Ctrl + X, dlatego zasugerowałem zmianę, ale to twoje pytanie. Głosowanie w dół byłoby głupie, gdyby było to zwykłe nieporozumienie.
Mikel

Odpowiedzi:

15

Awk (sprawdź także informacje o Awk ) jest piękne z tego rodzaju pytaniami. Próbować:

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

Definiuje to separator pól -Fjako [],] *- co oznacza jedno wystąpienie zamykającego nawiasu kwadratowego lub przecinka, po którym następuje zero lub dowolna liczba spacji. Oczywiście możesz to zmienić, aby spełnić wszelkie wymagania. Przeczytaj o wyrażeniach regularnych.

Po podzieleniu linii możesz zrobić co chcesz z wynikiem podziału. Tutaj postanowiłem wydrukować drugie pole tylko za pomocą print $2. Zauważ, że ważne jest stosowanie pojedynczych cudzysłowów wokół instrukcji awk, w przeciwnym razie 2 $ zostanie zastąpione przez powłokę.

asoundmove
źródło
2
]nie jest wspornikiem kątowym. Kątowniki są <>. []są „nawiasami kwadratowymi” lub po prostu „nawiasami kwadratowymi”.
cjm
Myślę, że musisz uciec od tego nawiasu zamykającego, chyba że rzeczywiście muszę czytać moje wyrażenia regularne.
Kit Sunde
@cjm - Może on jest Niemcem: news.ycombinator.com/item?id=1181243 :)
Kit Sunde
1
@cjm, przepraszam, chciałem powiedzieć nawias kwadratowy, wpisałem trochę za szybko. @Kit, nie jestem Niemcem. Nie chcesz uciec z wewnętrznego klamry zamykającej (nie miałoby to żadnego sensu), ale musi to być pierwsza postać w zasięgu.
asoundmove
12

Możesz zmodyfikować ostatni cutw swoim potoku do tego:

cut -d ' ' -f2-

Powyższe oznacza, że ​​separatorem pól jest spacja i chcemy wybrać wszystkie pola, zaczynając od drugiego. Pełna sekwencja staje się:

cut -d ',' -f1 cities | cut -d ' ' -f2-
Barun
źródło
12

W celu bardziej złożonej analizy należy użyć sed (1) :

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

Lub używając, -raby uprościć wyrażenie regularne, jak sugeruje pepoluan :

sed -re 's/\[[0-9]+\] ([^,]+),.*/\1/' cities
Juliano
źródło
2
+1. możesz także użyć opcji -r, aby zapobiec ucieczce zaawansowanych znaków
wyrażeń
0

Zwykle używam Perla, gdy robi się za ciężko, by sed i grep.

Istnieje wiele sposobów na napisanie go w Perlu. Na przykład wolisz, aby był szybki lub wolałby obsługiwać niewielkie nieoczekiwane problemy na wejściu (np. Dwie spacje, w których jedna była spodziewana).

Jeden oczywisty sposób (przy założeniu, że identyfikator jest liczbowy, miasto to alfabet, status to alfabet):

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

Lub wolniej, ale bardziej liberalnie (robi więcej cofania):

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

Lub szybciej (pole zatrzymuje się przy pierwszym wystąpieniu klamry zamykającej):

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

Z wiersza poleceń zamiast skryptu można użyć -nopcji, która w zasadzie dodaje while (<>) { BLOCK }pętlę:

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

lub jeśli chcesz, aby użycie przypominało cięcie, możesz użyć -Fopcji podobnej do -Fopcji awk , na przykład:

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

W ten sposób oczywiście zakłada się, że żadne pole nie będzie zawierało żadnego ogranicznika.

Mikel
źródło