GNU/Linux >> Linux Esercitazione >  >> Linux

Come analizzare un file CSV in Bash?

Come analizzare un file CSV in Bash?

Arrivando in ritardo a questa domanda e poiché bash offre nuove funzionalità, perché questa domanda riguarda bash e poiché nessuna delle risposte già pubblicate mostra questo modo potente e conforme di fare precisamente questo .

Analisi dei file CSV in bash , utilizzando il modulo caricabile

Conforme a RFC 4180 , una stringa come questa riga CSV di esempio :

12,22.45,"Hello, ""man"".","A, b.",42

dovrebbe essere diviso come

 1  12
 2  22.45
 3  Hello, "man".
 4  A, b.
 5  42

bash caricabile .C moduli compilati.

Sotto bash, puoi creare, modificare e utilizzare moduli compilati in c caricabili . Una volta caricati, funzionano come qualsiasi altro integrato !! (Puoi trovare maggiori informazioni nell'albero dei sorgenti.;)

L'albero dei sorgenti corrente (15 ottobre 2021, bash V5.1-rc3) contiene una serie di esempi:

accept        listen for and accept a remote network connection on a given port
asort         Sort arrays in-place
basename      Return non-directory portion of pathname.
cat           cat(1) replacement with no options - the way cat was intended.
csv           process one line of csv data and populate an indexed array.
dirname       Return directory portion of pathname.
fdflags       Change the flag associated with one of bash's open file descriptors.
finfo         Print file info.
head          Copy first part of files.
hello         Obligatory "Hello World" / sample loadable.
...
tee           Duplicate standard input.
template      Example template for loadable builtin.
truefalse     True and false builtins.
tty           Return terminal name.
uname         Print system information.
unlink        Remove a directory entry.
whoami        Print out username of current user.

C'è un cvs completamente funzionante parser pronto per l'uso in examples/loadables directory:csv.c !!

In un sistema basato su Debian GNU/Linux, potrebbe essere necessario installare il pacchetto bash-builtins tramite

apt install bash-builtins

Utilizzo di integrati bash caricabili :

Quindi:

enable -f /usr/lib/bash/csv csv

Da lì, puoi usare csv come integrato bash .

Con il mio campione:12,22.45,"Hello, ""man"".","A, b.",42

csv -a myArray '12,22.45,"Hello, ""man"".","A, b.",42'
printf "%s\n" "${myArray[@]}" | cat -n
     1      12
     2      22.45
     3      Hello, "man".
     4      A, b.
     5      42

Quindi in un ciclo, elaborando un file.

while IFS= read -r line;do
    csv -a aVar "$line"
    printf "First two columns are: [ '%s' - '%s' ]\n" "${aVar[0]}" "${aVar[1]}"
done <myfile.csv

Questo modo è chiaramente il più veloce e il più efficace rispetto all'utilizzo di qualsiasi altra combinazione di builtin bash o fork di qualsiasi binario.

Sfortunatamente, a seconda dell'implementazione del tuo sistema, se la tua versione di bash è stata compilata senza loadable , potrebbe non funzionare...

Esempio completo con campi CSV multilinea.

Ecco un piccolo file di esempio con 1 titolo, 4 colonne e 3 righe. Perché due campi contengono newline , i file sono 6 lunghezza delle righe.

Id,Name,Desc,Value
1234,Cpt1023,"Energy counter",34213
2343,Sns2123,"Temperatur sensor
to trigg for alarm",48.4
42,Eye1412,"Solar sensor ""Day /
Night""",12199.21

E un piccolo script in grado di analizzare correttamente questo file:

#!/bin/bash

enable -f /usr/lib/bash/csv csv

file="sample.csv"
exec {FD}<"$file"

read -ru $FD line
csv -a headline "$line"
printf -v fieldfmt '%-8s: "%%q"\\n' "${headline[@]}"

while read -ru $FD line;do
    while csv -a row "$line" ; ((${#row[@]}<${#headline[@]})) ;do
        read -ru $FD sline || break
        line+=$'\n'"$sline"
    done
    printf "$fieldfmt\\n" "${row[@]}"
done

Questo può rendere:(ho usato printf "%q" per rappresentare caratteri non stampabili come newline come $'\n' )

Id      : "1234"
Name    : "Cpt1023"
Desc    : "Energy\ counter"
Value   : "34213"

Id      : "2343"
Name    : "Sns2123"
Desc    : "$'Temperatur sensor\nto trigg for alarm'"
Value   : "48.4"

Id      : "42"
Name    : "Eye1412"
Desc    : "$'Solar sensor "Day /\nNight"'"
Value   : "12199.21"

Puoi trovare un esempio funzionante completo qui:csvsample.sh.txt orcsvsample.sh.

Avviso:

Ovviamente, l'analisi di CSV utilizzando questo non è perfetta! Questo funziona per molti semplici file CSV, ma attenzione alla codifica e alla sicurezza!! Ad esempio, questo modulo non sarà in grado di gestire i campi binari!

Leggi attentamente i commenti del codice sorgente csv.c e RFC 4180!


Possiamo analizzare i file csv con stringhe tra virgolette e delimitate da say | con il seguente codice

while read -r line
do
    field1=$(echo "$line" | awk -F'|' '{printf "%s", $1}' | tr -d '"')
    field2=$(echo "$line" | awk -F'|' '{printf "%s", $2}' | tr -d '"')

    echo "$field1 $field2"
done < "$csvFile"

awk analizza i campi stringa in variabili e tr rimuove la citazione.

Leggermente più lento di awk viene eseguito per ogni campo.


Dal man pagina:

-d delimIl primo carattere di delim è usato per terminare la riga di input, invece del newline.

Stai usando -d, che terminerà la riga di input sulla virgola. Non leggerà il resto della riga. Ecco perché $y è vuoto.


Devi usare IFS invece di -d :

while IFS=, read -r col1 col2
do
    echo "I got:$col1|$col2"
done < myfile.csv

Si noti che per l'analisi CSV per scopi generici è necessario utilizzare uno strumento specializzato in grado di gestire i campi tra virgolette con virgole interne, tra gli altri problemi che Bash non è in grado di gestire da solo. Esempi di tali strumenti sono cvstool e csvkit .


Linux
  1. Come si normalizza un percorso di file in Bash?

  2. Come evidenziare gli script Bash in Vim?

  3. Come controllare syslog in Bash su Linux?

  4. Come eseguire il grep di una sezione di un file nella shell bash

  5. Come grep per unicode � in uno script bash

Come verificare se un file o una directory esiste in Bash

Come leggere un file riga per riga in Bash

Come reindirizzare stderr a stdout in Bash

Come utilizzare gli operatori di test di file Bash in Linux

Come analizzare i file CSV negli script Bash in Linux

Come verificare se esiste un file o una directory in Bash Shell