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ść.
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ść.
Odpowiedzi:
Możesz do tego użyć awk. Zmień „$ 2” na n-tą kolumnę, którą chcesz.
awk -F "\"*,\"*" '{print $2}' textfile.csv
gawk -F"|" "{print $13}" files*.csv
...,"string,string",...
"
a ostatnia zakończy"
awk -F "\"*;\"*" '{print $2}' textfile.csv
tak. cat mycsv.csv | cut -d ',' -f3
wydrukuje trzecią kolumnę.
awk
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.
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.
format '%(2)\n'
polecenie nie mogło powiedzieć, gdzie kończy się jedno pole. (csvtool 1.4.2)
csvtool
wydają się wymagać użycia -
jako nazwy pliku wejściowego do odczytu ze standardowego wejścia.
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
Wylądowałem tutaj, szukając wyodrębnienia z pliku rozdzielonego tabulatorami. Pomyślałem, że dodam.
cat textfile.tsv | cut -f2 -s
Gdzie -f2
wyodrębnia 2, niezerową kolumnę indeksowaną lub drugą kolumnę.
cat
jest niepotrzebny:< textfile.tsv cut -f2 -s
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.
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 < file
część 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.
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 dos2unix
do 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.
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 ...]
Potrzebowałem odpowiedniego parsowania CSV, a nie cut
/ awk
i 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
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
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'
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
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ć csvcut
narzę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
Jeśli ciągi w csv są cytowane, dodaj znak cudzysłowu z q
opcją:
csvcut -q '"' -c 2 filename_in.csv > filename_out.csv
Zainstaluj za pomocą pip install csvkit
lub sudo apt install csvkit
.
Nie możesz tego zrobić bez pełnego parsera CSV.
cut
liczy?
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
Możesz także użyć pętli while
IFS=,
while read name val; do
echo "............................"
echo Name: "$name"
done<itemlst.csv
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
drukuje2
zamiast2,3,4,5
.