csv 파일이있는 경우 단일 열의 내용 만 인쇄하는 빠른 bash 방법이 있습니까? 각 행에 동일한 수의 열이 있다고 가정하는 것이 안전하지만 각 열의 내용은 길이가 다릅니다.
csv 파일이있는 경우 단일 열의 내용 만 인쇄하는 빠른 bash 방법이 있습니까? 각 행에 동일한 수의 열이 있다고 가정하는 것이 안전하지만 각 열의 내용은 길이가 다릅니다.
답변:
이를 위해 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
세 번째 열을 인쇄합니다.
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
사용해야하는 것 같습니다 -
.
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
탭으로 구분 된 파일에서 추출하기 위해 여기에 도착했습니다. 내가 추가 할 것이라고 생각했다.
cat textfile.tsv | cut -f2 -s
여기서 -f2
0이 아닌 인덱스 열 또는 두 번째 열을 추출합니다.
cat
불필요 :< textfile.tsv cut -f2 -s
이 질문에 대한 많은 답변은 훌륭하며 일부는 코너 케이스를 조사했습니다. 일상적으로 사용할 수있는 간단한 답변을 추가하고 싶습니다 ... 대부분 그 코너 케이스에 들어가는 경우 (예 : 쉼표 나 쉼표를 따옴표로 이스케이프 처리).
FS (Field Separator)는 값이 공백으로 손상되는 변수입니다. 따라서 기본적으로 awk는 모든 줄의 공간에서 분할됩니다.
따라서 BEGIN (입력하기 전에 실행)을 사용하여이 필드를 원하는대로 설정할 수 있습니다.
awk 'BEGIN {FS = ","}; {print $3}'
위의 코드는 csv 파일의 세 번째 열을 인쇄합니다.
다른 답변은 잘 작동하지만 bash 셸을 사용하여 솔루션을 요청했기 때문에 다음과 같이 할 수 있습니다.
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"이라고하는 배열을 만든 다음 while 루프의 "do"섹션으로 보냅니다.
do echo "${csv_line[0]}";done < file
-이제 우리는 "do"단계에 있으며 "csv_line"배열의 0 번째 요소를 echo합니다. 이 작업은 파일의 모든 줄에서 반복됩니다. < file
부분은 어디에서 읽을 수있는 while 루프를 말하고있다. 참고 : bash에서 배열은 인덱스가 0이므로 첫 번째 열은 0 번째 요소입니다.
그래서 거기에 쉘의 CSV에서 열을 가져옵니다. 다른 솔루션은 아마도 더 실용적 일 수 있지만 이것은 순수한 bash입니다.
GNU Awk를 사용할 수 있습니다 . 이 사용자 가이드 문서를 참조하십시오 . 기사 (2015 년 6 월)에 제시된 솔루션의 개선으로, 다음 gawk 명령은 큰 따옴표 필드 안에 큰 따옴표를 허용합니다. 큰 따옴표는 두 개의 연속적인 큰 따옴표 ( "")로 표시됩니다. 또한 이것은 빈 필드를 허용 하지만 이것조차도 여러 줄 필드를 처리 할 수 없습니다 . 다음 예제 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
"\ n"와 UTF-8 (바이트 순서 표시가없는) 각각에 가능한 DOS 스타일의 줄 바꿈을 변환 할 (CRLF 즉 "\ 연구 \ n") 및 (바이트 순서 마크) UTF-16 인코딩을. 표준 CSV 파일은 CRLF를 줄 바꿈으로 사용 합니다. Wikipedia를 참조하십시오 .
입력에 여러 줄 필드가 포함될 수있는 경우 다음 스크립트를 사용할 수 있습니다. 출력에서 레코드를 분리하기 위해 특수 문자열을 사용하는 것에 유의하십시오 (기본 분리 자 개행이 레코드 내에서 발생할 수 있기 때문입니다). 다시, 다음 예제 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 는 일반적인 Unix 텍스트 처리 도구를 사용하여 특정 열을 선택할 수 있도록 필드 내의 특수 문자가 변환되도록 수정 된 CSV 파일의 내용을 출력 할 수 있습니다. 예를 들어 다음 코드는 세 번째 열을 출력합니다.
csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u
csvquote
임의의 큰 파일을 처리하는 데 사용할 수 있습니다.
다음은 2 개의 열이있는 csv 파일 예입니다.
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는 필드를 나타내고 d는 구분자를 나타냅니다.
위의 명령을 실행하면 다음과 같은 출력이 생성됩니다.
산출
Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28
두 번째 열만 가져 오려면 :
cut -d, -f2 myTooth.csv
다음은 출력 출력입니다.
Tooth
wisdom
canine
canine
wisdom
incisor
또 다른 사용 사례 :
csv 입력 파일에는 10 개의 열이 있으며 쉼표를 구분 기호로 사용하여 2 ~ 5 열과 8 열을 원합니다. "
cut은 -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
기도합니다. 나는 이것을 사용하지 않는 맥에서 시도하고 csvtool
있지만 맥에는 루비가 함께 제공되므로 다음과 같이 할 수 있습니다.
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
그런 다음 첫 번째 열을 얻습니다.
[dumb@one pts]$ awk -F , '{print $1}' file
a
1
a
1
두 번째 열을 가져옵니다.
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
.
잠시 동안이 코드를 사용해 왔지만 "스택 오버플로에서 잘라 내기 및 붙여 넣기"를 계산하지 않는 한 "빠른"것은 아닙니다.
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
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
2
대신 인쇄 됩니다2,3,4,5
.