GNU/Linux >> Linux Esercitazione >  >> Linux

Come estrarre/modificare le righe in un file di testo i cui dati sono separati in campi?

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 a foobar ), 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 che perl 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 ciascuna print 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
    

Linux
  1. Come rimuovere le righe duplicate all'interno di un file di testo?

  2. Come eliminare più righe casuali da un file di testo utilizzando Sed?

  3. Cosa sono le modalità Vim? Come cambiarli?

  4. Come contare il numero di valori univoci di un campo in un file di testo delimitato da tabulazioni?

  5. Trasformare più righe in una riga separata da virgole

Scripting Bash:come leggere i dati dai file di testo

Come stampare righe duplicate in un file di testo in Linux

Come unire più righe in una in un file in Linux

Come estrarre indirizzi e-mail da file di testo in Linux

Come importare dati in Apache Solr

Come fare eco in un file