Bir csv dosyasının bir sütunu nasıl çıkarılır


111

Bir csv dosyam varsa, yalnızca tek bir sütunun içeriğini yazdırmanın hızlı bir bash yolu var mı? Her satırın aynı sayıda sütuna sahip olduğunu varsaymak güvenlidir, ancak her sütunun içeriği farklı uzunlukta olacaktır.

Yanıtlar:


137

Bunun için awk kullanabilirsiniz. '$ 2'yi istediğiniz n'inci sütuna değiştirin.

awk -F "\"*,\"*" '{print $2}' textfile.csv

13
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'2yerine yazdıracak 2,3,4,5.
Igor Mikushkin

Windows'ta GNU Tools kullanan şanslı biriyseniz, @IgorMikushkin ile aynı komutu aşağıdaki gibi çalıştırabilirsiniz:gawk -F"|" "{print $13}" files*.csv
Elidio Marquina

10
Sanırım bu, virgül içeren dizeler olduğunda başarısız oluyor, yani...,"string,string",...
sodiumnitrate

Bence 1. ve son sütun için, bunun bazı kusurları olacak. İlk sütun ile başlayacak "ve sonuncusu ile "
bitecek

Bazı programlar farklı sınırlayıcılara sahip CSV dosyalarını döndürür, bu nedenle normal ifadeyi buna göre değiştirmek gerekebilir. Noktalı virgül sınırlayıcı örneği: awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev

88

Evet. cat mycsv.csv | cut -d ',' -f33. sütunu yazdıracaktır.


8
İkinci sütun virgül içermediği sürece, bu durumda ikinci sütunun ikinci yarısını alırsınız. <col1>, "3,000", <col2> noktasındaki durum. Cevabım bu soruna göre çok daha iyi değil. Bu yüzden sinirlenmeyin.
synthesizerpatel


1
Farklı değerleri ayırt etmek için CSV dosyasının çift tırnak içerdiğinden emin değiliz. En uygun çözümü değerlendirebilmemiz için bir girdi dosyası sağlaması daha iyi olur.
Idriss Neumann

51

Bunu yapabilmemin en basit yolu sadece csvtool kullanmaktı . Csvtool'u kullanmak için başka kullanım durumlarım da vardı ve sütun verilerinin kendisinde görünürlerse tırnakları veya sınırlayıcıları uygun şekilde işleyebilir.

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

2'yi sütun numarasıyla değiştirmek, aradığınız sütun verilerini etkili bir şekilde çıkaracaktır.


14
Kabul edilen cevap bu olmalıdır. Bu araç, virgülü alan ayırıcı olarak işlemenin çok ötesinde CSV dosyalarıyla nasıl başa çıkılacağını bilir. 2. sütunu çıkarmak için, "csvtool col 2 input.csv"
Vladislavs Dovgalecs

3
Sadece bir uyarı ... standart girdi ile csvtool kullanmak istiyorsanız (örnek csv başka bir komuttan gelir) bu böyle bir şey cat input.csv | csvtool formath '%(2)\n' -Not Burada cat'in işe yaramaz olduğunu biliyorum ama normalde bir csv'yi dışa aktaracak herhangi bir komut için kullanın.
General Redneck

Çok satırlı alanlar vardır, format '%(2)\n'komut bir alanın nerede bittiğini söyleyemez. (CsvTool 1.4.2)
Jarno

1
Yeni sürümleri, stdin'den okumak için girdi dosya adı olarak csvtoolkullanmayı gerektiriyor gibi görünüyor -.
Connor Clark

@GeneralRedneck neden kedi kullanıyorsunuz? ve formatı formath değilcsvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec

15

Sekmeyle ayrılmış bir dosyadan çıkarmak isteyen buraya indi. Ekleyeceğimi düşündüm.

cat textfile.tsv | cut -f2 -s

Burada -f22, sıfır olmayan endeksli sütun veya ikinci sütun ekstreleri.


basit, çok önemli ve diğer örneklerden daha kolay uyarlanabilir. Teşekkürler!
Nick Jennings

6
Nitpicking, ama catgereksiz:< textfile.tsv cut -f2 -s
Anne van Rossum

8

Bu sorulara verilecek pek çok cevap harika ve hatta bazıları köşedeki vakalara baktı. Günlük kullanımda olabilen basit bir cevap eklemek istiyorum ... çoğunlukla bu köşedeki durumlara giriyorsunuz (tırnak içinde kaçan virgül veya virgül vb.).

FS (Alan Ayırıcı), değeri uzaya düşürülen değişkendir. Yani awk varsayılan olarak herhangi bir satır için boşlukta böler.

Yani BEGIN (Giriş almadan önce yürüt) kullanarak bu alanı istediğimiz herhangi bir şeye ayarlayabiliriz ...

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

Yukarıdaki kod 3. sütunu bir csv dosyasında yazdıracaktır.


1
Bunu denedim ve hala alıntı yapılan alanlar içinde virgül kullanıyor.
Daniel C. Sobral

5

Diğer yanıtlar iyi çalışıyor, ancak yalnızca bash kabuğunu kullanarak bir çözüm istediğinizden, bunu yapabilirsiniz:

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

Ve sonra şu şekilde sütunları (bu örnekte ilki) çekebilirsiniz:

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

Yani burada birkaç şey oluyor:

  • while IFS=,- bu, kabuğun alanları (metin blokları) neyin ayırdığını bilmek için kullandığı IFS (Dahili Alan Ayırıcı) olarak virgül kullanmayı söylüyor. Yani IFS = demek, IFS = "" ise "a, b" nin "a b" ile aynı olduğunu söylemek gibidir (varsayılan olarak budur.)

  • read -a csv_line; - bu, her satırda birer birer oku ve her öğenin "csv_line" olarak adlandırıldığı bir dizi oluşturup bunu while döngümüzün "do" bölümüne gönder diyor.

  • do echo "${csv_line[0]}";done < file- şimdi "yap" aşamasındayız ve "csv_line" dizisinin 0. elemanını yankıla diyoruz. Bu işlem dosyanın her satırında tekrarlanır. < fileBölüm sadece yerden okumaya while döngüsü anlatıyor. NOT: bash'de dizilerin 0 dizinli olduğunu unutmayın, bu nedenle ilk sütun 0. öğedir.

İşte burada, kabuktaki bir CSV'den bir sütun çıkarıyorsunuz. Diğer çözümler muhtemelen daha pratiktir, ancak bu tamamen bash.


5

GNU Awk'ı kullanabilirsiniz , kullanım kılavuzunun bu makalesine bakın . Makalede (Haziran 2015'te) sunulan çözümde bir iyileştirme olarak, aşağıdaki gawk komutu çift tırnaklı alanlar içinde çift tırnak kullanımına izin verir; çift ​​tırnak, iki ardışık çift tırnak ("") ile işaretlenir. Dahası, bu boş alanlara izin verir, ancak bu bile çok satırlı alanları işleyemez . Aşağıdaki örnek c=3, textfile.csv dosyasının 3. sütununu (aracılığıyla ) yazdırır :

#!/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)

Kullanımına dikkat dos2unix"\ n" ve UTF-8, (bayt sırası işareti olmadan) sırasıyla mümkün DOS tarzı satır sonları dönüştürmek için (CRLF yani "\ r \ n") ve (bayt sırası işareti), UTF-16 kodlaması. Standart CSV dosyaları satır sonu olarak CRLF kullanır, Wikipedia'ya bakın .

Giriş çok satırlı alanlar içeriyorsa, aşağıdaki komut dosyasını kullanabilirsiniz. Çıktıda kayıtları ayırmak için özel dizge kullanımına dikkat edin (çünkü bir kayıt içinde varsayılan ayırıcı satırsonu olabilir). Yine, aşağıdaki örnek c=3textfile.csv dosyasının 3. sütununu (üzerinden ) yazdırır :

#!/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)

Soruna başka bir yaklaşım var. csvquote , alandaki özel karakterlerin dönüştürülmesi için değiştirilmiş bir CSV dosyasının içeriğini çıkarabilir , böylece belirli bir sütunu seçmek için normal Unix metin işleme araçları kullanılabilir. Örneğin, aşağıdaki kod üçüncü sütunun çıktısını verir:

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

csvquote rastgele büyük dosyaları işlemek için kullanılabilir.


5

İşte 2 sütunlu bir csv dosyası örneği

myTooth.csv

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

İlk sütunu almak için şunu kullanın:

cut -d, -f1 myTooth.csv

f Alan anlamına gelir ve d sınırlayıcı anlamına gelir

Yukarıdaki komutu çalıştırmak aşağıdaki çıktıyı üretecektir.

Çıktı

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

Yalnızca 2. sütunu almak için:

cut -d, -f2 myTooth.csv

Ve işte çıktı Çıktı

Tooth
wisdom
canine
canine
wisdom
incisor

Başka bir kullanım durumu:

Csv girdi dosyanız 10 sütun içeriyor ve 2'den 5'e kadar olan sütunları ve ayırıcı olarak virgül kullanarak 8'inci sütunları istiyorsunuz ".

cut, sütunları belirtmek için -f ("alanlar" anlamına gelir) ve ayırıcıyı belirtmek için -d ("sınırlayıcı" anlamına gelir) kullanır. İkincisini belirtmeniz gerekir çünkü bazı dosyalar sütunları ayırmak için boşluklar, sekmeler veya iki nokta üst üste kullanabilir.

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

cut bir komut yardımcı programıdır ve işte birkaç örnek daha:

SYNOPSIS
     cut -b list [-n] [file ...]
     cut -c list [file ...]
     cut -f list [-d delim] [-s] [file ...]

4

Düzgün CSV ayrıştırma değil, gerekli cut/ awkve dua. Bunu bir mac'ta deniyorum csvtool, ancak mac'ler Ruby ile geliyor, yani şunları yapabilirsiniz:

echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby

4

Önce temel bir CSV oluşturacağız

[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

Sonra 1. sütunu alıyoruz

[dumb@one pts]$  awk -F , '{print $1}' file  
a  
1  
a  
1

3
csvtool col 2 file.csv 

2 ilgilendiğiniz sütun nerede

sen de yapabilirsin

csvtool col 1,2 file.csv 

birden çok sütun yapmak


3

Bence en kolayı csvkit kullanmak :

2. sütunu alır: csvcut -c 2 file.csv

Bununla birlikte, csvtool ve muhtemelen bir dizi başka csv bash aracı da vardır:

sudo apt-get install csvtool (Debian tabanlı sistemler için)

Bu, içinde 'ID' bulunan ilk satırın bulunduğu bir sütun döndürür. csvtool namedcol ID csv_file.csv

Bu, dördüncü satırı döndürür: csvtool col 4 csv_file.csv

Başlık satırını bırakmak isterseniz:

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


2

Şimdiye kadarki cevapların hiçbirinde neden csvkit'ten bahsetmediğini merak ediyorum.

csvkit, CSV'ye dönüştürmek ve CSV ile çalışmak için bir komut satırı araçları paketidir

csvkit belgeleri

Ben sadece csv veri yönetimi için kullanıyorum ve şu ana kadar cvskit kullanarak çözemediğim bir problem bulamadım.

Bir cvs dosyasından bir veya daha fazla sütun çıkarmak csvcutiçin araç kutusunun bir parçası olan yardımcı programı kullanabilirsiniz . İkinci sütunu çıkarmak için şu komutu kullanın:

csvcut -c 2 filename_in.csv > filename_out.csv 

csvcut referans sayfası

Csv'deki dizeler tırnak içine alınmışsa, qseçenekle tırnak karakterini ekleyin :

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

pip install csvkitVeya ile yükleyin sudo apt install csvkit.


1

Tam bir CSV ayrıştırıcısı olmadan bunu yapamazsınız.


1
Bir şey ne zaman tam bir CSV ayrıştırıcısı olarak sayılır? Sayılır mı cut?
HelloGoodbye

0

Bu kodu bir süredir kullanıyorum, "stackoverflow'dan kesme ve yapıştırma" yı saymadığınız sürece "hızlı" değildir.

Bir döngüde IFS yerine $ {##} ve $ {%%} operatörlerini kullanır. 'Err' ve 'die' çağırır ve SEP karakterleri olarak yalnızca virgül, tire ve boruyu destekler (ihtiyacım olan tek şey bu).

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*}
}

Misal:

$ 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

0

Ayrıca while döngüsü de kullanabilirsiniz

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

        echo Name: "$name"
done<itemlst.csv

Bu kod bir Shellcheck uyarısı oluşturur: SC2034 . Arama, uyarıdan kaçmanın yollarını ararken bu soruyu ilk sonuç olarak döndürür.
jww
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.