Jak wyodrębnić jedną kolumnę z pliku csv

111

Jeśli mam plik csv, czy istnieje szybki sposób na wydrukowanie zawartości tylko jednej kolumny? Można bezpiecznie założyć, że każdy wiersz ma taką samą liczbę kolumn, ale zawartość każdej kolumny miałaby inną długość.

user788171
źródło

Odpowiedzi:

137

Możesz do tego użyć awk. Zmień „$ 2” na n-tą kolumnę, którą chcesz.

awk -F "\"*,\"*" '{print $2}' textfile.csv
synthesizerpatel
źródło
13
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'drukuje 2zamiast 2,3,4,5.
Igor Mikushkin
Jeśli jesteś szczęściarzem używającym narzędzi GNU w systemie Windows, możesz wykonać to samo polecenie co @IgorMikushkin w następujący sposób:gawk -F"|" "{print $13}" files*.csv
Elidio Marquina,
10
Myślę, że to się nie udaje, gdy istnieją ciągi zawierające przecinek, tj....,"string,string",...
azotan sodu
Myślę, że dla pierwszej i ostatniej kolumny będzie to miało pewną wadę. Pierwsza kolumna rozpocznie się, "a ostatnia zakończy"
BigTailWolf
Niektóre programy zwracają pliki CSV z różnymi ogranicznikami, więc może być wymagana odpowiednia zmiana wyrażenia regularnego. Przykład separatora średnika: awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev
88

tak. cat mycsv.csv | cut -d ',' -f3wydrukuje trzecią kolumnę.

madrag
źródło
8
Chyba że druga kolumna zawiera przecinek, w takim przypadku otrzymasz drugą połowę drugiej kolumny. Wielkość liter w punkcie <col1>, "3,000", <col2>. Moja odpowiedź nie jest jednak lepsza w odniesieniu do tego problemu. Więc nie daj się nabrać.
synthesizerpatel
@synthesizerpatel Zgadzam się lepiej używaćawk
MattSizzle
1
Nie jesteśmy pewni, czy jego plik CSV zawiera podwójne cudzysłowy, aby rozróżnić różne wartości. Byłoby lepiej, gdyby dostarczył plik wejściowy, abyśmy mogli ocenić najbardziej odpowiednie rozwiązanie.
Idriss Neumann
51

Najprostszym sposobem, w jaki udało mi się to zrobić, było użycie csvtool . Miałem również inne przypadki użycia, aby użyć csvtool i może odpowiednio obsługiwać cudzysłowy lub ograniczniki, jeśli pojawiają się w samych danych kolumny.

csvtool format '%(2)\n' input.csv

Zastąpienie 2 numerem kolumny skutecznie wyodrębni dane kolumny, której szukasz.

Samar
źródło
14
To powinna być akceptowana odpowiedź. To narzędzie wie, jak radzić sobie z plikami CSV, znacznie wykraczając poza traktowanie przecinka jako separatora pól. Aby wyodrębnić drugą kolumnę, „csvtool col 2 input.csv”
Vladislavs Dovgalecs,
3
Tylko uwaga ... jeśli chcesz użyć csvtool ze standardowym wejściem (na przykład csv pochodzi z innego polecenia), jest to coś takiego. cat input.csv | csvtool formath '%(2)\n' -Uwaga Wiem, że cat tutaj jest bezużyteczny, ale podporządkuj go dla dowolnego polecenia, które normalnie wyeksportowałoby csv.
Generał Redneck
Jeśli istnieją pola wielowierszowe, format '%(2)\n'polecenie nie mogło powiedzieć, gdzie kończy się jedno pole. (csvtool 1.4.2)
jarno
1
Nowsze wersje csvtoolwydają się wymagać użycia -jako nazwy pliku wejściowego do odczytu ze standardowego wejścia.
Connor Clark
@GeneralRedneck po co używać kota? i jego format nie jest formatemcsvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec
15

Wylądowałem tutaj, szukając wyodrębnienia z pliku rozdzielonego tabulatorami. Pomyślałem, że dodam.

cat textfile.tsv | cut -f2 -s

Gdzie -f2wyodrębnia 2, niezerową kolumnę indeksowaną lub drugą kolumnę.

cevaris
źródło
też prosty i łatwiejszy do dostosowania niż inne przykłady. dzięki!
Nick Jennings,
6
Nitpicking, ale catjest niepotrzebny:< textfile.tsv cut -f2 -s
Anne van Rossum
8

Wiele odpowiedzi na te pytania jest świetnych, a niektórzy nawet zajrzeli do narożnych przypadków. Chciałbym dodać prostą odpowiedź, która może być przydatna na co dzień ... gdzie najczęściej trafiasz do tych narożnych przypadków (np. Unikanie przecinków lub przecinków w cudzysłowie itp.).

FS (Separator pól) to zmienna, której wartość jest zapisywana na spację. Zatem awk domyślnie dzieli w przestrzeni dowolną linię.

Więc używając BEGIN (Wykonaj przed pobraniem danych wejściowych) możemy ustawić to pole na cokolwiek chcemy ...

awk 'BEGIN {FS = ","}; {print $3}'

Powyższy kod wydrukuje trzecią kolumnę w pliku csv.

router
źródło
1
Próbowałem tego i nadal rozważa przecinki w cytowanych polach.
Daniel C. Sobral,
5

Inne odpowiedzi działają dobrze, ale ponieważ poprosiłeś o rozwiązanie przy użyciu tylko powłoki bash, możesz to zrobić:

AirBoxOmega:~ d$ cat > file #First we'll create a basic CSV
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10

Następnie możesz wyciągnąć kolumny (pierwsze w tym przykładzie) w następujący sposób:

AirBoxOmega:~ d$ while IFS=, read -a csv_line;do echo "${csv_line[0]}";done < file
a
1
a
1
a
1
a
1
a
1
a
1

Tak więc dzieje się tutaj kilka rzeczy:

  • while IFS=,- oznacza to użycie przecinka jako IFS (wewnętrznego separatora pól), którego używa powłoka, aby wiedzieć, co oddziela pola (bloki tekstu). Zatem powiedzenie IFS = jest jak powiedzenie „a, b” to to samo, co „a b” byłoby, gdyby IFS = „” (czyli jest tym, czym jest domyślnie).

  • read -a csv_line; - to znaczy czytaj w każdym wierszu, pojedynczo i stwórz tablicę, w której każdy element nazywa się „csv_line” i wyślij to do sekcji „do” naszej pętli while

  • do echo "${csv_line[0]}";done < file- teraz jesteśmy w fazie „do” i mówimy echo zerowego elementu tablicy „csv_line”. Ta akcja jest powtarzana w każdym wierszu pliku. Ta < fileczęść mówi po prostu pętli while, z której należy czytać. UWAGA: pamiętaj, że w bash tablice są indeksowane do 0, więc pierwsza kolumna jest zerowym elementem.

Więc masz to, wyciągając kolumnę z CSV w powłoce. Inne rozwiązania są prawdopodobnie bardziej praktyczne, ale to jest czysty bash.

drldcsta
źródło
5

Możesz użyć GNU Awk, zobacz ten artykuł w przewodniku użytkownika . Jako ulepszenie rozwiązania przedstawionego w artykule (w czerwcu 2015 r.), Następujące polecenie gawk umożliwia stosowanie podwójnych cudzysłowów w polach z podwójnymi cudzysłowami; podwójny cudzysłów jest tam oznaczony dwoma kolejnymi podwójnymi cudzysłowami (""). Co więcej, pozwala to na puste pola, ale nawet to nie obsługuje pól wielowierszowych . Poniższy przykład wyświetla trzecią kolumnę (za pośrednictwem c=3) pliku textfile.csv:

#!/bin/bash
gawk -- '
BEGIN{
    FPAT="([^,\"]*)|(\"((\"\")*[^\"]*)*\")"
}
{
    if (substr($c, 1, 1) == "\"") {
        $c = substr($c, 2, length($c) - 2) # Get the text within the two quotes
        gsub("\"\"", "\"", $c)  # Normalize double quotes
    }
    print $c
}
' c=3 < <(dos2unix <textfile.csv)

Zwróć uwagę na użycie dos2unixdo konwersji możliwych podziałów linii w stylu DOS (CRLF tj. „\ R \ n”) i kodowania UTF-16 (ze znacznikiem kolejności bajtów) odpowiednio na „\ n” i UTF-8 (bez znaku kolejności bajtów). Standardowe pliki CSV używają CRLF jako podziału wiersza, zobacz Wikipedia .

Jeśli dane wejściowe mogą zawierać pola wielowierszowe, możesz użyć następującego skryptu. Zwróć uwagę na użycie specjalnego ciągu do oddzielania rekordów w wynikach (ponieważ domyślny separator nowej linii może występować w rekordzie). Ponownie, poniższy przykład wyświetla trzecią kolumnę (za pośrednictwem c=3) pliku textfile.csv:

#!/bin/bash
gawk -- '
BEGIN{
    RS="\0" # Read the whole input file as one record;
    # assume there is no null character in input.
    FS="" # Suppose this setting eases internal splitting work.
    ORS="\n####\n" # Use a special output separator to show borders of a record.
}
{
    nof=patsplit($0, a, /([^,"\n]*)|("(("")*[^"]*)*")/, seps)
    field=0;
    for (i=1; i<=nof; i++){
        field++
        if (field==c) {
            if (substr(a[i], 1, 1) == "\"") {
                a[i] = substr(a[i], 2, length(a[i]) - 2) # Get the text within 
                # the two quotes.
                gsub(/""/, "\"", a[i])  # Normalize double quotes.
            }
            print a[i]
        }
        if (seps[i]!=",") field=0
    }
}
' c=3 < <(dos2unix <textfile.csv)

Istnieje inne podejście do problemu. csvquote może wyświetlać zawartość pliku CSV zmodyfikowanego w taki sposób, że znaki specjalne w polu są przekształcane w taki sposób, że do wybrania określonej kolumny można użyć zwykłych narzędzi przetwarzania tekstu Unix. Na przykład poniższy kod zwraca trzecią kolumnę:

csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u

csvquote może być używany do przetwarzania dowolnych dużych plików.

jarno
źródło
5

Oto przykład pliku CSV z 2 kolumnami

myTooth.csv

Date,Tooth
2017-01-25,wisdom
2017-02-19,canine
2017-02-24,canine
2017-02-28,wisdom

Aby uzyskać pierwszą kolumnę, użyj:

cut -d, -f1 myTooth.csv

f oznacza pole, a d oznacza ogranicznik

Uruchomienie powyższego polecenia spowoduje wyświetlenie następującego wyniku.

Wynik

Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28

Aby uzyskać tylko drugą kolumnę:

cut -d, -f2 myTooth.csv

A oto wyjście wyjściowe

Tooth
wisdom
canine
canine
wisdom
incisor

Inny przypadek użycia:

Twój plik wejściowy csv zawiera 10 kolumn, a chcesz mieć kolumny od 2 do 5 i kolumny 8, używając przecinka jako separatora ”.

cut używa -f (co oznacza „pola”) do określenia kolumn i -d (co oznacza „separator”) do określenia separatora. Musisz określić to drugie, ponieważ niektóre pliki mogą używać spacji, tabulatorów lub dwukropków do oddzielania kolumn.

cut -f 2-5,8 -d , myvalues.csv

cut to narzędzie poleceń, a oto kilka przykładów:

SYNOPSIS
     cut -b list [-n] [file ...]
     cut -c list [file ...]
     cut -f list [-d delim] [-s] [file ...]
Stryker
źródło
4

Potrzebowałem odpowiedniego parsowania CSV, a nie cut/ awki modlitwy. Próbuję tego na komputerze Mac bez csvtool, ale komputery Mac są dostarczane z rubinem, więc możesz:

echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby
Darth Egregious
źródło
4

Najpierw utworzymy podstawowy plik CSV

[dumb@one pts]$ cat > file 
a,b,c,d,e,f,g,h,i,k  
1,2,3,4,5,6,7,8,9,10  
a,b,c,d,e,f,g,h,i,k  
1,2,3,4,5,6,7,8,9,10

Następnie otrzymujemy pierwszą kolumnę

[dumb@one pts]$  awk -F , '{print $1}' file  
a  
1  
a  
1
Raj Velayudhan
źródło
3
csvtool col 2 file.csv 

gdzie 2 to interesująca Cię kolumna

możesz też zrobić

csvtool col 1,2 file.csv 

zrobić wiele kolumn

exussum
źródło
3

Myślę, że najłatwiej jest użyć csvkit :

Pobiera drugą kolumnę: csvcut -c 2 file.csv

Jednak istnieje również csvtool i prawdopodobnie wiele innych narzędzi csv bash:

sudo apt-get install csvtool (dla systemów opartych na Debianie)

Spowoduje to zwrócenie kolumny z pierwszym wierszem zawierającym „ID”. csvtool namedcol ID csv_file.csv

To zwróci czwarty wiersz: csvtool col 4 csv_file.csv

Jeśli chcesz usunąć wiersz nagłówka:

csvtool col 4 csv_file.csv | sed '1d'

słowa do rozumu
źródło
2

Zastanawiam się, dlaczego żadna z dotychczasowych odpowiedzi nie wspominała o csvkit.

csvkit to zestaw narzędzi wiersza poleceń do konwersji do formatu CSV i pracy z nim

Dokumentacja csvkit

Używam go wyłącznie do zarządzania danymi csv i do tej pory nie znalazłem problemu, którego nie mógłbym rozwiązać za pomocą cvskit.

Aby wyodrębnić jedną lub więcej kolumn z pliku cvs, możesz użyć csvcutnarzędzia, które jest częścią zestawu narzędzi. Aby wyodrębnić drugą kolumnę, użyj tego polecenia:

csvcut -c 2 filename_in.csv > filename_out.csv 

Strona referencyjna csvcut

Jeśli ciągi w csv są cytowane, dodaj znak cudzysłowu z qopcją:

csvcut -q '"' -c 2 filename_in.csv > filename_out.csv 

Zainstaluj za pomocą pip install csvkitlub sudo apt install csvkit.

Soundbytes
źródło
1

Nie możesz tego zrobić bez pełnego parsera CSV.

Peter Krumins
źródło
1
Kiedy coś liczy się jako pełny parser CSV? Nie cutliczy?
HelloGoodbye
0

Używam tego kodu od jakiegoś czasu, nie jest to „szybkie”, chyba że policzysz „wycinanie i wklejanie z przepełnienia stosu”.

Używa operatorów $ {##} i $ {%%} w pętli zamiast IFS. Wzywa „err” i „die” i obsługuje tylko przecinki, myślniki i kreski jako znaki SEP (to wszystko, czego potrzebowałem).

err()  { echo "${0##*/}: Error:" "$@" >&2; }
die()  { err "$@"; exit 1; }

# Return Nth field in a csv string, fields numbered starting with 1
csv_fldN() { fldN , "$1" "$2"; }

# Return Nth field in string of fields separated
# by SEP, fields numbered starting with 1
fldN() {
        local me="fldN: "
        local sep="$1"
        local fldnum="$2"
        local vals="$3"
        case "$sep" in
                -|,|\|) ;;
                *) die "$me: arg1 sep: unsupported separator '$sep'" ;;
        esac
        case "$fldnum" in
                [0-9]*) [ "$fldnum" -gt 0 ] || { err "$me: arg2 fldnum=$fldnum must be number greater or equal to 0."; return 1; } ;;
                *) { err "$me: arg2 fldnum=$fldnum must be number"; return 1;} ;;
        esac
        [ -z "$vals" ] && err "$me: missing arg2 vals: list of '$sep' separated values" && return 1
        fldnum=$(($fldnum - 1))
        while [ $fldnum -gt 0 ] ; do
                vals="${vals#*$sep}"
                fldnum=$(($fldnum - 1))
        done
        echo ${vals%%$sep*}
}

Przykład:

$ CSVLINE="example,fields with whitespace,field3"
$ $ for fno in $(seq 3); do echo field$fno: $(csv_fldN $fno "$CSVLINE");  done
field1: example
field2: fields with whitespace
field3: field3
qneill
źródło
0

Możesz także użyć pętli while

IFS=,
while read name val; do
        echo "............................"

        echo Name: "$name"
done<itemlst.csv
K.Sopheak
źródło
Ten kod generuje ostrzeżenie Shellcheck : SC2034 . Wyszukiwanie zwraca to pytanie jako pierwszy wynik podczas szukania sposobów na obejście ostrzeżenia.
jww