Jak analyzovat soubor CSV v Bash?
Přicházíme pozdě na tuto otázku a bash nabízí nové funkce, protože tato otázka se týká bash a protože žádná z již zveřejněných odpovědí neukazuje tento účinný a vyhovující způsob, jak udělat přesně toto .
Analýza souborů CSV pod bash
pomocí zaváděcího modulu
Vyhovuje RFC 4180 , řetězec jako tento ukázkový řádek CSV :
12,22.45,"Hello, ""man"".","A, b.",42
by měl být rozdělen jako
1 12
2 22.45
3 Hello, "man".
4 A, b.
5 42
bash lze načíst .C kompilované moduly.
V bash můžete vytvářet, upravovat a používat načítatelné c kompilované moduly . Po načtení fungují jako každá jiná budova !! (Více informací můžete najít ve zdrojovém stromu.;)
Aktuální zdrojový strom (15. října 2021, bash V5.1-rc3) obsahuje spoustu ukázek:
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.
K dispozici je plně funkční cvs
analyzátor připraven k použití v examples/loadables
adresář:csv.c !!
V systému založeném na Debian GNU/Linux budete možná muset nainstalovat balíček bash-builtins pomocí
apt install bash-builtins
Použití načítatelných bash-builtinů :
Potom:
enable -f /usr/lib/bash/csv csv
Odtud můžete použít csv
jako vestavěný bash .
S mým vzorem: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
Poté ve smyčce zpracování souboru.
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
Tento způsob je jednoznačně nejrychlejší a nejsilnější než použití jakékoli jiné kombinace vestavěných bash nebo forku k libovolnému binárnímu systému.
Bohužel, v závislosti na implementaci vašeho systému, pokud vaše verze bash byla zkompilována bez loadable
, to nemusí fungovat...
Úplný vzorek s víceřádkovými poli CSV.
Zde je malý ukázkový soubor s 1 nadpis, 4 sloupce a 3 řádky. Protože dvě pole obsahují nový řádek , soubor je 6 délka řádků.
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
A malý skript schopný správně analyzovat tento soubor:
#!/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
To může vykreslit:(Použil jsem printf "%q"
reprezentovat netisknutelné znaky jako nové řádky jako $'\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"
Úplnou pracovní ukázku můžete najít zde:csvsample.sh.txt nebo csvsample.sh.
Upozornění:
Parsování CSV pomocí tohoto samozřejmě není dokonalé! Toto funguje pro mnoho jednoduchých souborů CSV, ale postarejte se o kódování a zabezpečení! Například tento modul nebude schopen zpracovat binární pole!
Přečtěte si pozorně komentáře ke zdrojovému kódu csv.c a RFC 4180!
Můžeme analyzovat soubory csv s řetězci v uvozovkách a oddělenými řekněme | s následujícím kódem
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
analyzuje pole řetězců na proměnné a tr
odstraní nabídku.
O něco pomalejší než awk
se provede pro každé pole.
Z man
stránka:
-d delimPrvní znak delim se používá k ukončení vstupního řádku, nikoli nový řádek.
Používáte -d,
která ukončí vstupní řádek na čárce. Nepřečte zbytek řádku. Proto je $y prázdné.
Musíte použít IFS
místo -d
:
while IFS=, read -r col1 col2
do
echo "I got:$col1|$col2"
done < myfile.csv
Všimněte si, že pro obecnou analýzu CSV byste měli použít specializovaný nástroj, který dokáže zpracovat pole v uvozovkách s vnitřními čárkami, mimo jiné problémy, které Bash sám nezvládne. Příklady takových nástrojů jsou cvstool
a csvkit
.