หากฉันมีไฟล์ csv มีวิธีทุบตีด่วนในการพิมพ์เนื้อหาจากคอลัมน์เดียวหรือไม่ สามารถสันนิษฐานได้อย่างปลอดภัยว่าแต่ละแถวมีจำนวนคอลัมน์เท่ากัน แต่เนื้อหาของแต่ละคอลัมน์จะมีความยาวต่างกัน
หากฉันมีไฟล์ csv มีวิธีทุบตีด่วนในการพิมพ์เนื้อหาจากคอลัมน์เดียวหรือไม่ สามารถสันนิษฐานได้อย่างปลอดภัยว่าแต่ละแถวมีจำนวนคอลัมน์เท่ากัน แต่เนื้อหาของแต่ละคอลัมน์จะมีความยาวต่างกัน
คำตอบ:
คุณสามารถใช้ awk สำหรับสิ่งนี้ เปลี่ยน '$ 2' เป็นคอลัมน์ที่ n ที่คุณต้องการ
awk -F "\"*,\"*" '{print $2}' textfile.csv
gawk -F"|" "{print $13}" files*.csv
...,"string,string",...
"
และสุดท้ายจะลงท้ายด้วย"
awk -F "\"*;\"*" '{print $2}' textfile.csv
ใช่. cat mycsv.csv | cut -d ',' -f3
จะพิมพ์คอลัมน์ที่ 3
awk
วิธีที่ง่ายที่สุดที่ฉันสามารถทำได้คือใช้ csvtool ฉันมีกรณีการใช้งานอื่น ๆ เช่นกันในการใช้ csvtool และสามารถจัดการเครื่องหมายคำพูดหรือตัวคั่นได้อย่างเหมาะสมหากปรากฏในข้อมูลคอลัมน์เอง
csvtool format '%(2)\n' input.csv
การแทนที่ 2 ด้วยหมายเลขคอลัมน์จะดึงข้อมูลคอลัมน์ที่คุณต้องการได้อย่างมีประสิทธิภาพ
cat input.csv | csvtool formath '%(2)\n' -
หมายเหตุฉันรู้ว่า cat ที่นี่ไม่มีประโยชน์ แต่ย่อยสำหรับคำสั่งใด ๆ ที่ปกติจะส่งออก csv
format '%(2)\n'
บรรทัดคำสั่งไม่สามารถบอกได้ว่าฟิลด์ใดฟิลด์หนึ่งสิ้นสุดลง (csvtool 1.4.2)
csvtool
ดูเหมือนจะต้องใช้-
เป็นชื่อไฟล์อินพุตเพื่ออ่านจาก stdin
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
มาถึงที่นี่เพื่อดึงข้อมูลจากไฟล์ที่คั่นด้วยแท็บ คิดว่าฉันจะเพิ่ม
cat textfile.tsv | cut -f2 -s
โดย-f2
แยกคอลัมน์ 2 คอลัมน์ที่ไม่ได้จัดทำดัชนีที่ไม่ใช่ศูนย์หรือคอลัมน์ที่สอง
cat
ไม่จำเป็น:< textfile.tsv cut -f2 -s
คำตอบหลายข้อสำหรับคำถามนี้ดีมากและบางคนยังมองในมุมกลับ ฉันต้องการเพิ่มคำตอบง่ายๆที่สามารถใช้ได้ทุกวัน ...
FS (Field Separator) คือตัวแปรที่มีค่า dafaulted เป็นช่องว่าง ดังนั้นโดยค่าเริ่มต้น awk จะแบ่งที่ว่างสำหรับบรรทัดใด ๆ
ดังนั้นการใช้ BEGIN (ดำเนินการก่อนป้อนข้อมูล) เราสามารถตั้งค่าฟิลด์นี้เป็นอะไรก็ได้ที่เราต้องการ ...
awk 'BEGIN {FS = ","}; {print $3}'
โค้ดด้านบนจะพิมพ์คอลัมน์ที่ 3 ในไฟล์ csv
คำตอบอื่น ๆ ใช้ได้ดี แต่เนื่องจากคุณขอวิธีแก้ปัญหาโดยใช้เพียง bash shell คุณสามารถทำได้:
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
จากนั้นคุณสามารถดึงคอลัมน์ (รายการแรกในตัวอย่างนี้) ดังนี้:
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
มีสองสิ่งเกิดขึ้นที่นี่:
while IFS=,
- นี่คือการใช้เครื่องหมายจุลภาคเป็น IFS (Internal Field Separator) ซึ่งเป็นสิ่งที่เชลล์ใช้เพื่อทราบว่าอะไรที่แยกฟิลด์ (บล็อกของข้อความ) ดังนั้นการพูดว่า IFS = ก็เหมือนกับการพูดว่า "a, b" เหมือนกับ "a b" ก็คือถ้า IFS = "" (ซึ่งเป็นค่าเริ่มต้น)
read -a csv_line;
- นี่คือการบอกว่าอ่านในแต่ละบรรทัดทีละบรรทัดและสร้างอาร์เรย์โดยแต่ละองค์ประกอบเรียกว่า "csv_line" และส่งไปยังส่วน "do" ของ while loop ของเรา
do echo "${csv_line[0]}";done < file
- ตอนนี้เราอยู่ในเฟส "do" และเรากำลังพูดว่า echo องค์ประกอบที่ 0 ของอาร์เรย์ "csv_line" การดำเนินการนี้ซ้ำในทุกบรรทัดของไฟล์ < file
ส่วนหนึ่งเป็นเพียงการบอกห่วงขณะที่อ่านจาก หมายเหตุ: จำไว้ว่าใน bash อาร์เรย์เป็น 0 ดัชนีดังนั้นคอลัมน์แรกจึงเป็นองค์ประกอบที่ 0
คุณมีมันแล้วดึงคอลัมน์จาก CSV ในเชลล์ออกมา วิธีแก้ปัญหาอื่น ๆ น่าจะเป็นประโยชน์มากกว่า แต่อันนี้เป็นการทุบตีล้วนๆ
คุณสามารถใช้ GNU Awk ดูบทความของคู่มือผู้ใช้นี้ ในการปรับปรุงโซลูชันที่นำเสนอในบทความ (ในเดือนมิถุนายน 2015) คำสั่ง gawk ต่อไปนี้อนุญาตให้ใส่เครื่องหมายคำพูดคู่ในช่องที่มีเครื่องหมายอัญประกาศคู่ เครื่องหมายคำพูดคู่จะทำเครื่องหมายด้วยเครื่องหมายคำพูดคู่ ("") สองครั้งที่นั่น นอกจากนี้ยังอนุญาตให้มีช่องว่างแต่ก็ไม่สามารถจัดการกับเขตข้อมูลหลายเส้นได้ ตัวอย่างต่อไปนี้พิมพ์คอลัมน์ที่ 3 (ผ่านc=3
) ของ 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)
สังเกตการใช้dos2unix
เพื่อแปลงตัวแบ่งสไตล์ DOS ที่เป็นไปได้ (CRLF เช่น "\ r \ n") และการเข้ารหัส UTF-16 (พร้อมเครื่องหมายลำดับไบต์) เป็น "\ n" และ UTF-8 (โดยไม่มีเครื่องหมายลำดับไบต์) ตามลำดับ มาตรฐานการใช้งานไฟล์ CSV CRLF เป็นเส้นแบ่งดูวิกิพีเดีย
หากอินพุตอาจมีหลายช่องคุณสามารถใช้สคริปต์ต่อไปนี้ สังเกตการใช้สตริงพิเศษสำหรับการแยกเร็กคอร์ดในเอาต์พุต (เนื่องจากตัวคั่นดีฟอลต์ขึ้นบรรทัดใหม่อาจเกิดขึ้นภายในเร็กคอร์ด) อีกครั้งตัวอย่างต่อไปนี้พิมพ์คอลัมน์ที่ 3 (ผ่านc=3
) ของ 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)
มีแนวทางอื่นในการแก้ปัญหา csvquoteสามารถส่งออกเนื้อหาของไฟล์ CSV ที่แก้ไขเพื่อให้มีการแปลงอักขระพิเศษภายในฟิลด์เพื่อให้สามารถใช้เครื่องมือประมวลผลข้อความ Unix ตามปกติเพื่อเลือกคอลัมน์บางคอลัมน์ได้ ตัวอย่างเช่นโค้ดต่อไปนี้แสดงคอลัมน์ที่สาม:
csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u
csvquote
สามารถใช้เพื่อประมวลผลไฟล์ขนาดใหญ่โดยพลการ
นี่คือตัวอย่างไฟล์ csv ที่มี 2 คอลัมน์
myTooth.csv
Date,Tooth
2017-01-25,wisdom
2017-02-19,canine
2017-02-24,canine
2017-02-28,wisdom
ในการรับคอลัมน์แรกให้ใช้:
cut -d, -f1 myTooth.csv
f ย่อมาจาก Field และ d หมายถึงตัวคั่น
การรันคำสั่งดังกล่าวจะสร้างผลลัพธ์ต่อไปนี้
เอาต์พุต
Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28
ในการรับคอลัมน์ที่ 2 เท่านั้น:
cut -d, -f2 myTooth.csv
และนี่คือเอาต์พุต เอาต์พุต
Tooth
wisdom
canine
canine
wisdom
incisor
กรณีการใช้งานอื่น:
ไฟล์อินพุต csv ของคุณมี 10 คอลัมน์และคุณต้องการคอลัมน์ 2 ถึง 5 และคอลัมน์ 8 โดยใช้คอมมาเป็นตัวคั่น "
ตัดใช้ -f (หมายถึง "เขตข้อมูล") เพื่อระบุคอลัมน์และ -d (หมายถึง "ตัวคั่น") เพื่อระบุตัวคั่น คุณต้องระบุหลังเนื่องจากไฟล์บางไฟล์อาจใช้ช่องว่างแท็บหรือโคลอนเพื่อแยกคอลัมน์
cut -f 2-5,8 -d , myvalues.csv
cut เป็นยูทิลิตี้คำสั่งและนี่คือตัวอย่างเพิ่มเติม:
SYNOPSIS
cut -b list [-n] [file ...]
cut -c list [file ...]
cut -f list [-d delim] [-s] [file ...]
ฉันต้องการการแยกวิเคราะห์ CSV ที่เหมาะสมไม่ใช่cut
/ awk
และคำอธิษฐาน ฉันกำลังลองใช้กับ mac โดยไม่มีcsvtool
แต่mac มาพร้อมทับทิมดังนั้นคุณสามารถทำได้:
echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby
ขั้นแรกเราจะสร้าง 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
จากนั้นเราจะได้คอลัมน์ที่ 1
[dumb@one pts]$ awk -F , '{print $1}' file
a
1
a
1
csvtool col 2 file.csv
โดยที่ 2 คือคอลัมน์ที่คุณสนใจ
คุณยังสามารถทำได้
csvtool col 1,2 file.csv
เพื่อทำหลายคอลัมน์
ฉันคิดว่าง่ายที่สุดคือใช้csvkit :
รับคอลัมน์ที่ 2:
csvcut -c 2 file.csv
อย่างไรก็ตามยังมีcsvtoolและอาจมีเครื่องมือ csv bash อื่น ๆ อีกมากมาย:
sudo apt-get install csvtool
(สำหรับระบบที่ใช้ Debian)
สิ่งนี้จะส่งคืนคอลัมน์โดยแถวแรกมี 'ID' อยู่
csvtool namedcol ID csv_file.csv
สิ่งนี้จะส่งคืนแถวที่สี่:
csvtool col 4 csv_file.csv
หากคุณต้องการวางแถวส่วนหัว:
csvtool col 4 csv_file.csv | sed '1d'
ฉันสงสัยว่าทำไมไม่มีคำตอบใดที่กล่าวถึง csvkit
csvkit เป็นชุดเครื่องมือบรรทัดคำสั่งสำหรับการแปลงและทำงานกับ CSV
ฉันใช้มันเฉพาะสำหรับการจัดการข้อมูล csv และจนถึงตอนนี้ฉันยังไม่พบปัญหาที่ฉันไม่สามารถแก้ไขได้โดยใช้ cvskit
ในการแยกคอลัมน์อย่างน้อยหนึ่งคอลัมน์จากไฟล์ cvs คุณสามารถใช้csvcut
ยูทิลิตี้ที่เป็นส่วนหนึ่งของกล่องเครื่องมือ ในการแยกคอลัมน์ที่สองให้ใช้คำสั่งนี้:
csvcut -c 2 filename_in.csv > filename_out.csv
หากสตริงใน csv ถูกยกมาให้เพิ่มอักขระเครื่องหมายคำพูดด้วยq
ตัวเลือก:
csvcut -q '"' -c 2 filename_in.csv > filename_out.csv
ติดตั้งด้วยpip install csvkit
หรือsudo apt install csvkit
.
คุณไม่สามารถทำได้หากไม่มีตัวแยกวิเคราะห์ CSV แบบเต็ม
cut
นับ?
เมื่อใช้รหัสนี้มาระยะหนึ่งแล้วจะไม่ "ด่วน" เว้นแต่คุณจะนับ "การตัดและวางจาก stackoverflow"
ใช้ตัวดำเนินการ $ {##} และ $ {%%} ในการวนซ้ำแทน IFS มันเรียกว่า 'err' และ 'die' และรองรับเฉพาะเครื่องหมายจุลภาคเส้นประและท่อเป็นตัวอักษร SEP (นั่นคือทั้งหมดที่ฉันต้องการ)
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*}
}
ตัวอย่าง:
$ 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
คุณยังสามารถใช้ในขณะวนซ้ำ
IFS=,
while read name val; do
echo "............................"
echo Name: "$name"
done<itemlst.csv
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
จะพิมพ์แทน2
2,3,4,5