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"' file
Modifica del delimitatore in
:
awk -F":" '$3=="foo"' file
L'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,
foo
corrisponde 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 cheperl
dovrebbe 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 ciascunaprint
chiamare;-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"' file
Modifica del delimitatore in
:
perl -F":" -ane 'print if $F[2] eq "foo"' file
A differenza di
awk
,perl
non è 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
@F
array e quindi stampare l'array. Con semplici file separati da spazi, questo è facile:perl -lane '$F[2]="foo"; print "@F"' file
Con un delimitatore diverso, dovrai
join
la 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' file
Il
-n
sopprime l'output normale e il/regex/p
significa "stampa tutte le righe che corrispondono alla regex. -
Come posso stampare solo righe il cui 2° campo non è
foo
?sed '/^[^:]*:foo:/d' file
L'inverso logico di quanto sopra. Qui, il
/regex/d
significa "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/' file
Oppure, poiché
sed
la sostituzione può indirizzare direttamente un'occorrenza di pattern mediante la sua ripetizione con un semplice flag numerico:sed 's/[^:]*/foo/2' file