Cosa devo usare quando il taglio non lo taglia?


19

Ho un file citiescome questo:

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

Voglio tagliare i nomi delle città, in modo da avere:

San Diego
St Louis
Orlando

Questo è il meglio che ho potuto inventare:

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

Ma questo mi lascia ancora con uno spazio davanti ai nomi. Esiste un cutcomando simile che posso usare che accetta delimitatori di più caratteri in modo da poterlo tagliare ]?


1
trè utile per eliminare i caratteri che non vuoi.
LawrenceC,

Se provi il codice nelle risposte delle persone, vedrai tre diversi output. Ciò suggerisce che la tua domanda non era chiara al 100%. "Tagliare" significa rimuovere o selezionare? Vuoi lo (inactive)stato o no? Fornisci un esempio di output.
Mikel,

@Mikel - Considerando che sto usando cutper tagliare le cose e puoi vedere l'intento dell'esempio fallito che ho, dovrebbe essere abbastanza chiaro nel contesto. Fornirò un campione per chiarirlo ulteriormente. :)
Kit Sunde,

No, non proprio. Ho cambiato una frase nella tua domanda per "stampare solo i nomi delle città", perché non era chiaro il tuo uso della parola "taglio". La mia modifica è corretta?
Mikel,

1
@Kit Sunde: con l'output di esempio, è certamente comprensibile. Il titolo è carino. "ritagliare" mi fa pensare a cosa succede quando si preme Ctrl + X, motivo per cui ho suggerito la modifica, ma è la tua domanda. Il downvoting sarebbe sciocco quando è solo un semplice disaccordo.
Mikel,

Risposte:


15

Awk (controlla anche Awk Info ) è bello con quel tipo di domanda. Provare:

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

Questo definisce un separatore di campo -Fcome [],] *- che significa una ricorrenza di una parentesi quadra di chiusura o di una virgola, seguita da zero o da un numero qualsiasi di spazi. Ovviamente puoi cambiarlo per soddisfare qualsiasi esigenza. Leggi le espressioni regolari.

Una volta divisa la linea, puoi fare quello che vuoi con il risultato della divisione. Qui, ho deciso di stampare il secondo campo solo con print $2. Si noti che è importante usare virgolette singole attorno alle istruzioni awk, altrimenti $ 2 viene sostituito dalla shell.


2
]non è una parentesi angolare. Le parentesi angolari sono <>. []sono "parentesi quadre" o semplicemente "parentesi".
cjm

Penso che tu debba sfuggire a quella parentesi di chiusura, a meno che in realtà non abbia bisogno di leggere le mie espressioni regolari.
Kit Sunde,

@cjm - Forse è tedesco: news.ycombinator.com/item?id=1181243 :)
Kit Sunde

1
@cjm, scusa volevo dire parentesi quadra, digitata un po 'troppo velocemente. @Kit, non sono tedesco. Non vuoi sfuggire alla parentesi di chiusura interna (non servirebbe a nulla), ma deve essere il primo personaggio nell'intervallo.
asoundmove,

12

È possibile modificare l'ultimo cutnella pipeline in questo modo:

cut -d ' ' -f2-

Quanto sopra indica che il separatore di campi è uno spazio bianco e vogliamo selezionare tutti i campi a partire dal secondo. La sequenza completa diventa:

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

12

Per analisi più complesse, è necessario utilizzare sed (1) :

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

O usando -rper semplificare l'espressione regolare, come suggerito da pepoluan :

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

2
+1. puoi anche usare -r per evitare di sfuggire ai caratteri regex avanzati, semplificando notevolmente il modello regex
pepoluan

0

Di solito uso Perl quando le cose diventano troppo difficili per sed e grep.

Esistono diversi modi per scriverlo in Perl. Ad esempio, potresti preferire che sia veloce, oppure potresti preferire gestire lievi problemi imprevisti nell'input (ad esempio due spazi in cui uno era previsto).

Un modo ovvio (presuppone che id sia numerico, la città è alfabetica, lo stato è alfabetico):

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

O più lento ma più permissivo (fa più backtracking):

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

O più veloce (il campo si arresta alla prima occorrenza della parentesi quadra di chiusura):

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

Dalla riga di comando anziché da uno script, è possibile utilizzare l' -nopzione, che in sostanza aggiunge il while (<>) { BLOCK }ciclo:

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

o se vuoi che l'uso assomigli al taglio, puoi usare l' -Fopzione, che è simile -Fall'opzione di awk , ad esempio:

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

In questo modo ovviamente si presume che nessun campo conterrà nessuno dei delimitatori.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.