Come posso manipolare i dati basati sul campo dalla riga di comando? Ad esempio
- Come posso stampare solo righe il cui ennesimo campo è
foo? - Come posso stampare solo righe il cui ennesimo campo non è
foo? - Come posso stampare solo righe il cui ennesimo campo corrisponde a
foo? - Come posso cambiare il campo N in
foo?
Esiste un approccio o un set di strumenti standard che facilita la manipolazione dei dati basati sul campo su sistemi *nix?
Risposta accettata:
Ci sono due approcci di base che si possono usare quando si ha a che fare con i campi:i) usare uno strumento che comprenda i campi; ii) utilizzare un'espressione regolare. Dei due, il primo è solitamente più robusto e più semplice.
Molti degli strumenti comunemente disponibili su *nix sono progettati esplicitamente per gestire i campi o hanno trucchi ingegnosi per facilitarlo.
1. Usa uno strumento che comprenda i campi
1.1 awk
Lo strumento classico qui è awk . Dividerà automaticamente ogni riga di input in campi (il separatore di campo è uno spazio bianco per impostazione predefinita ma può essere modificato usando il -F flag) e i campi sono quindi disponibili per awk script come $n dove n è il numero del campo. Il primo campo è $1 , il secondo $2 ecc.
-
Stampa le righe il cui 3° campo è
foo.awk '$3=="foo"' fileModifica del delimitatore in
:awk -F":" '$3=="foo"' fileL'azione predefinita di
awkè stampare. Pertanto i comandi precedenti stamperanno tutte le righe il cui 3° campo èfoo. Quando si utilizza-F, puoi impostare separatori di campo arbitrari e persino utilizzare espressioni regolari. -
Come posso stampare solo righe il cui 3° campo non è
foo?awk '$3!="foo"' file -
Come posso stampare solo righe il cui 3° campo corrisponde a
foo?Se stai solo cercando campi che corrispondono a un modello (ad esempio,
foocorrisponde afoobar), usa~invece di==:awk '$3~/foo/' file -
Come posso stampare solo righe il cui 3° campo non corrisponde a
foo?awk '$3!~/foo/' file -
Come posso cambiare il 3° campo in
foo?awk '$3="foo"' file
1.2 Perl
Un'altra scelta è perl battute. Come awk, Perl è un linguaggio di scripting completo ma può anche essere eseguito come programma a riga di comando prendendo uno script come input. Il suo comportamento è modificato dalle opzioni della riga di comando, le più rilevanti delle quali per questa domanda sono:
-e:lo script cheperldovrebbe essere eseguito;-n:legge il file di input riga per riga;-p:stampa ogni riga di input dopo aver applicato lo script fornito da-e;-l:rimuove le nuove righe finali da ciascuna riga di input e aggiungi una nuova riga a ciascunaprintchiamare;-a:awk-mode, divide ogni riga di input nell'array@F;-F:il separatore di campo per-a.
Una differenza importante con awk è quello perl 's -a switch divide i file in un array. In Perl, gli array iniziano da 0, non da 1. Ciò significa che il 2° campo è in realtà $F[1] e non $F[2] . Con tutto questo in mente, il perl equivalenti di quanto sopra sono:
-
Stampa le righe il cui 3° campo è
foo.perl -ane 'print if $F[2] eq "foo"' fileModifica del delimitatore in
:perl -F":" -ane 'print if $F[2] eq "foo"' fileA differenza di
awk,perlnon è possibile utilizzare le espressioni regolari come delimitatori di campo. Devono essere un carattere o una stringa specifici. -
Come posso stampare solo righe il cui 3° campo non è
foo?perl -ane 'print unless $F[2] eq "foo"' file -
Come posso stampare solo righe il cui 3° campo corrisponde a
foo?perl -ane 'print if $F[2]=~/foo/' file -
Come posso stampare solo righe il cui 3° campo non corrisponde a
foo?perl -lane 'print unless $F[2]=~/foo/' file -
Come posso cambiare il 3° campo in
foo?Questo è un po' più ingombrante in Perl. L'approccio usuale consiste nel modificare il valore in
@Farray e quindi stampare l'array. Con semplici file separati da spazi, questo è facile:perl -lane '$F[2]="foo"; print "@F"' fileCon un delimitatore diverso, dovrai
joinla matrice. In caso contrario, verrà stampato separato da spazi:perl -F: -lane '$F[2]="foo"; print join ":",@F' file
2. Usa le espressioni regolari
L'idea qui è di usare un'espressione regolare ("regex" in breve) che definisce la posizione della stringa di destinazione nella riga. Ad esempio, in un file i cui campi sono separati da : , possiamo trovare il 2° campo abbinando tutto fino al 1° : (il 1° campo) e poi cercando il secondo:
^[^:]*:[^:]*:
Questa espressione regolare significa:
^:l'inizio della riga;[^]:una classe di caratteri negata.[^:]significa "tutto tranne:“;*:0 o più del modello precedente;::un letterale:;
Presi insieme, questo significa che il primo [^:]* è il primo campo e il secondo è il secondo campo. Ovviamente, questo non è molto pratico se stai cercando il 14° campo ma può essere utile per le cose più semplici. Quindi, come lo implementiamo per manipolare i nostri dati? Ci sono vari strumenti che possono farlo; in questi esempi userò sed ma potresti fare cose molto simili con awk , perl o python .
-
Come posso stampare solo righe il cui 2° campo è
foo?sed -n '/^[^:]*:foo:/p' fileIl
-nsopprime l'output normale e il/regex/psignifica "stampa tutte le righe che corrispondono alla regex. -
Come posso stampare solo righe il cui 2° campo non è
foo?sed '/^[^:]*:foo:/d' fileL'inverso logico di quanto sopra. Qui, il
/regex/dsignifica "elimina tutte le righe che corrispondono alla regex. -
Come posso stampare solo righe il cui 2° campo corrisponde a
foo?sed -n '/^[^:]*:[^:]*foo/p' file -
Come posso stampare solo righe il cui 2° campo non corrisponde a
foo?sed '/^[^:]*:[^:]*foo/d' file -
Come posso cambiare il 2° campo in
foo?sed 's/([^:]*:)[^:]*/1foo/' fileOppure, poiché
sedla sostituzione può indirizzare direttamente un'occorrenza di pattern mediante la sua ripetizione con un semplice flag numerico:sed 's/[^:]*/foo/2' file