Jak przeanalizować plik CSV w Bash?

112

Pracuję nad długim scenariuszem Bash. Chcę odczytać komórki z pliku CSV do zmiennych Bash. Mogę analizować wiersze i pierwszą kolumnę, ale nie mogę analizować żadnej innej kolumny. Oto mój dotychczasowy kod:


  cat myfile.csv|while read line
  do
    read -d, col1 col2 < <(echo $line)
    echo "I got:$col1|$col2"
  done

Drukuje tylko pierwszą kolumnę. Jako dodatkowy test wypróbowałem następujące:

read -d, x y < <(echo a,b,)

A $ y jest puste. Więc spróbowałem:

read x y < <(echo a b)

A $ y jest b. Czemu?

Użytkownik1
źródło
7
czy za awkużycie $1, $2itp?
BeemerGuy
4
as a sidenote: command <(echo "string") ---> command <<< "string"
tokland
1
W tym celu zaprojektowano program wiersza poleceń „cut”: ss64.com/bash/cut.html
Jay

Odpowiedzi:

215

Musisz użyć IFSzamiast -d:

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

Zauważ, że do ogólnego parsowania CSV powinieneś użyć specjalistycznego narzędzia, które może obsługiwać cytowane pola z wewnętrznymi przecinkami, między innymi problemami, których Bash nie może sobie sam poradzić. Przykładami takich narzędzi są cvstooli csvkit.

Wstrzymano do odwołania.
źródło
7
Proponowane rozwiązanie jest dobre dla bardzo prostych plików CSV, to znaczy jeśli nagłówki i wartości są wolne od przecinków i osadzonych cudzysłowów. W rzeczywistości napisanie ogólnego parsera CSV jest dość trudne (zwłaszcza, że ​​istnieje kilka „standardów” CSV). Jednym ze sposobów uczynienia plików CSV bardziej podatnymi na narzędzia * nix jest konwersja ich do formatu TSV (wartości rozdzielane tabulatorami), np. Przy użyciu programu Excel.
szczyt
Ciekawe, że nie mogę zrobić mkdir w ciele. Dostaję command not found. Tylko echoprace.
Zsolt
1
@Zsolt: Nie ma powodu, dla którego tak powinno być. Musisz mieć literówkę lub zabłąkany niedrukowalny znak.
Wstrzymano do odwołania.
2
@DennisWilliamson Należy załączyć separator np. Podczas korzystania z ;:while IFS=";" read col1 col2; do ...
thomas.mc.work
1
@ thomas.mc.work: To prawda w przypadku średników i innych znaków, które są specjalne dla powłoki. W przypadku przecinka nie jest to konieczne i wolę pomijać niepotrzebne znaki. Na przykład, zawsze możesz określić zmienne do rozwinięcia za pomocą nawiasów klamrowych (np. ${var}), Ale pomijam je, gdy nie są potrzebne. Dla mnie wygląda czyściej.
Wstrzymano do odwołania.
10

Ze manstrony:

-d separator Pierwszy znak separatora służy do zakończenia wiersza wejściowego zamiast znaku nowej linii.

Używasz, -d,który zakończy linię wejściową przecinkiem. Nie odczyta reszty wiersza. Dlatego $ y jest pusty.

dogbane
źródło
3

Możemy analizować pliki csv z ciągami znaków w cudzysłowach i rozdzielonymi powiedzmy | z następującym kodem

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 przetwarza pola łańcuchowe na zmienne i tr usuwa cudzysłów.

Nieco wolniej, ponieważ awk jest wykonywany dla każdego pola.

Maithilish
źródło
1
Dobrze, możesz też użyć koma (,)
pkarc
0

Jeśli chcesz przeczytać plik CSV z kilkoma liniami, to jest to rozwiązanie.

while IFS=, read -ra line
do 
    test $i -eq 1 && ((i=i+1)) && continue
    for col_val in ${line[@]}
    do
        echo -n "$col_val|"                 
    done
    echo        
done < "$csvFile"
Eliya
źródło