Was soll ich verwenden, wenn cut es nicht schneidet?

19

Ich habe eine Datei citieswie diese:

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

Ich möchte die Städtenamen ausschneiden, damit ich:

San Diego
St Louis
Orlando

Dies ist das Beste, was ich mir einfallen lassen konnte:

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

Aber das lässt mir immer noch ein Leerzeichen vor den Namen. Gibt es einen cutähnlichen Befehl, den ich verwenden kann, um Begrenzer mit mehreren Zeichen zu akzeptieren, damit ich ihn einschränken kann ]?

Kit Sunde
quelle
1
trist nützlich zum Löschen von Zeichen, die Sie nicht möchten.
LawrenceC
Wenn Sie den Code in den Antworten der Leute ausprobieren, sehen Sie drei verschiedene Ausgaben. Dies deutet darauf hin, dass Ihre Frage nicht 100% klar war. Bedeutet "Ausschneiden" Entfernen oder Auswählen? Willst du den (inactive)Status oder nicht? Bitte geben Sie eine Beispielausgabe an.
Mikel
@Mikel - Wenn ich bedenke, dass ich cutDinge ausschneide und Sie die Absicht des fehlgeschlagenen Beispiels sehen können, sollte es im Kontext ziemlich klar sein. Ich werde jedoch eine Probe zur Verfügung stellen, um es weiter zu klären. :)
Kit Sunde
Nein nicht wirklich. Ich habe einen Satz in Ihrer Frage in "Nur die Städtenamen drucken" geändert, weil mir die Verwendung des Wortes "Ausschneiden" unklar war. Ist meine Änderung korrekt?
Mikel
1
@Kit Sunde: Mit der Beispielausgabe ist es sicherlich verständlich. Der Titel ist süß. "Ausschneiden" lässt mich darüber nachdenken, was passiert, wenn Sie Strg + X drücken. Deshalb habe ich die Änderung vorgeschlagen, aber es ist Ihre Frage. Downvoting wäre dumm, wenn es nur eine einfache Meinungsverschiedenheit ist.
Mikel

Antworten:

15

Awk ( siehe auch Awk Info ) ist wunderschön mit solchen Fragen. Versuchen:

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

Dies definiert ein Feldtrennzeichen -Fals [],] *- was bedeutet, dass entweder eine schließende eckige Klammer oder ein Komma vorkommt, gefolgt von Null oder einer beliebigen Anzahl von Leerzeichen. Natürlich können Sie dies nach Bedarf ändern. Informieren Sie sich über reguläre Ausdrücke.

Sobald die Linie geteilt ist, können Sie mit dem Teilungsergebnis tun, was Sie wollen. Hier habe ich beschlossen, das zweite Feld nur mit auszudrucken print $2. Beachten Sie, dass es wichtig ist, die awk-Anweisungen in einfache Anführungszeichen zu setzen, da ansonsten $ 2 durch die Shell ersetzt wird.

asoundmove
quelle
2
]ist keine spitze Klammer. Spitze Klammern sind <>. []sind "eckige Klammern" oder nur "Klammern".
cjm
Ich denke, Sie müssen dieser schließenden Klammer entkommen, es sei denn, ich muss tatsächlich meine regulären Ausdrücke nachlesen.
Kit Sunde
@ cjm - Vielleicht ist er Deutsch: news.ycombinator.com/item?id=1181243 :)
Kit Sunde
1
@cjm, sorry ich wollte eckige Klammer sagen, etwas zu schnell getippt. @Kit, ich bin kein Deutscher. Sie möchten der inneren schließenden Klammer nicht entkommen (dies würde keinen Zweck erfüllen), aber es muss das erste Zeichen im Bereich sein.
Asoundmove
12

Sie können den letzten cutin Ihrer Pipeline folgendermaßen ändern :

cut -d ' ' -f2-

Das obige bedeutet, dass das Feldtrennzeichen ein Leerzeichen ist, und wir möchten alle Felder ab dem zweiten auswählen. Die komplette Sequenz wird:

cut -d ',' -f1 cities | cut -d ' ' -f2-
Barun
quelle
12

Für eine komplexere Analyse sollten Sie sed (1) verwenden :

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

Oder verwenden Sie -r, um den regulären Ausdruck zu vereinfachen, wie von pepoluan vorgeschlagen :

sed -re 's/\[[0-9]+\] ([^,]+),.*/\1/' cities
Juliano
quelle
2
+1. Sie können auch -r verwenden, um das Entweichen fortgeschrittener Regex-Zeichen zu verhindern, was das Regex-Muster erheblich vereinfacht
pepoluan
0

Normalerweise benutze ich Perl, wenn es für sed und grep zu schwierig wird.

Es gibt verschiedene Möglichkeiten, wie Sie es in Perl schreiben können. Beispielsweise möchten Sie möglicherweise, dass es schnell ist, oder Sie möchten möglicherweise, dass geringfügige unerwartete Probleme bei der Eingabe behoben werden (z. B. zwei Leerzeichen, bei denen eines erwartet wurde).

Ein offensichtlicher Weg (setzt voraus, dass die ID numerisch, die Stadt alphabetisch und der Status alphabetisch ist):

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

Oder langsamer, aber freizügiger (macht mehr Backtracking):

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

Oder schneller (Feld stoppt beim ersten Auftreten der schließenden Klammer):

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

Anstelle eines Skripts können Sie von der Befehlszeile aus die -nOption verwenden, die im Grunde die while (<>) { BLOCK }Schleife hinzufügt :

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

oder wenn Sie möchten, dass die Verwendung wie bei cut aussieht, können Sie die -FOption verwenden, die der von awk ähnelt. -FBeispiel:

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

Auf diese Weise wird offensichtlich davon ausgegangen, dass kein Feld eines der Begrenzer enthält.

Mikel
quelle