csv 파일의 한 열을 추출하는 방법


111

csv 파일이있는 경우 단일 열의 내용 만 인쇄하는 빠른 bash 방법이 있습니까? 각 행에 동일한 수의 열이 있다고 가정하는 것이 안전하지만 각 열의 내용은 길이가 다릅니다.

답변:


135

이를 위해 awk를 사용할 수 있습니다. '$ 2'를 원하는 n 번째 열로 변경합니다.

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

13
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'2대신 인쇄 됩니다 2,3,4,5.
Igor Mikushkin 2015 년

Windows에서 GNU 도구를 사용하는 운이 좋은 사람이라면 @IgorMikushkin과 동일한 명령을 다음과 같이 실행할 수 있습니다.gawk -F"|" "{print $13}" files*.csv
Elidio Marquina

10
나는 이것이, 즉 쉼표가 포함 된 문자열이있는 경우 실패 생각...,"string,string",...
질산 나트륨

나는 첫 번째와 마지막 열에 대해 약간의 결함이있을 것이라고 생각합니다. 첫 번째 열은 다음으로 시작 "하고 마지막은 다음으로 끝납니다."
BigTailWolf

일부 프로그램은 구분 기호가 다른 CSV 파일을 반환하므로 이에 따라 정규식을 변경해야 할 수 있습니다. 세미콜론 구분 기호의 예 : awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev

88

예. cat mycsv.csv | cut -d ',' -f3세 번째 열을 인쇄합니다.


8
두 번째 열에 쉼표가 포함되어 있지 않으면 두 번째 열의 두 번째 절반을 얻습니다. 대소 문자 <col1>, "3,000", <col2>. 그래도 그 문제와 관련하여 내 대답은 그다지 좋지 않습니다. 그러니 당황하지 마십시오.
synthesizerpatel

내가 사용하는 것이 좋습니다 동의 @synthesizerpatelawk
MattSizzle

1
그의 CSV 파일에 다른 값을 구별하기 위해 큰 따옴표가 포함되어 있는지 확실하지 않습니다. 가장 적절한 솔루션을 평가할 수 있도록 입력 파일을 제공하는 것이 더 좋습니다.
Idriss Neumann

50

이 작업을 수행 할 수있는 가장 간단한 방법은 csvtool을 사용하는 입니다. csvtool을 사용하는 다른 사용 사례도 있었으며 열 데이터 자체에 나타나는 경우 따옴표 또는 구분 기호를 적절하게 처리 할 수 ​​있습니다.

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

2를 열 번호로 바꾸면 찾고있는 열 데이터가 효과적으로 추출됩니다.


14
이것은 받아 들여진 대답이어야합니다. 이 도구는 쉼표를 필드 구분자로 취급하는 것 외에도 CSV 파일을 처리하는 방법을 알고 있습니다. 2 열, "csvtool COL 2 input.csv"추출하려면
Vladislavs Dovgalecs

3
참고로 ... 표준 입력 (예 : csv는 다른 명령에서 제공됨)과 함께 csvtool을 사용하려면 다음과 같습니다. cat input.csv | csvtool formath '%(2)\n' -참고 여기서 cat은 쓸모가 없지만 일반적으로 csv를 내보내는 모든 명령에 대해 하위 항목을 사용합니다.
General Redneck

여러 줄 필드가 있으며 format '%(2)\n'명령은 하나의 필드가 끝나는 위치를 알 수 없습니다. (csvtool 1.4.2)
jarno

1
의 최신 버전은 stdin에서 읽을 입력 파일 이름으로 csvtool사용해야하는 것 같습니다 -.
Connor Clark

@GeneralRedneck 왜 고양이를 사용합니까? 형식이 아닙니다csvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec

14

탭으로 구분 된 파일에서 추출하기 위해 여기에 도착했습니다. 내가 추가 할 것이라고 생각했다.

cat textfile.tsv | cut -f2 -s

여기서 -f20이 아닌 인덱스 열 또는 두 번째 열을 추출합니다.


간단하고 요점이며 다른 예보다 쉽게 ​​적용 할 수 있습니다. 감사!
Nick Jennings

6
Nitpicking, 그러나 cat불필요 :< textfile.tsv cut -f2 -s
Anne van Rossum

8

이 질문에 대한 많은 답변은 훌륭하며 일부는 코너 케이스를 조사했습니다. 일상적으로 사용할 수있는 간단한 답변을 추가하고 싶습니다 ... 대부분 그 코너 케이스에 들어가는 경우 (예 : 쉼표 나 쉼표를 따옴표로 이스케이프 처리).

FS (Field Separator)는 값이 공백으로 손상되는 변수입니다. 따라서 기본적으로 awk는 모든 줄의 공간에서 분할됩니다.

따라서 BEGIN (입력하기 전에 실행)을 사용하여이 필드를 원하는대로 설정할 수 있습니다.

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

위의 코드는 csv 파일의 세 번째 열을 인쇄합니다.


1
나는 이것을 시도했지만 여전히 인용 된 필드 안에 쉼표를 고려합니다.
Daniel C. Sobral

5

다른 답변은 잘 작동하지만 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입니다.


5

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 임의의 큰 파일을 처리하는 데 사용할 수 있습니다.


5

다음은 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 ...]

4

나는 적절한 CSV 구문 분석하지 필요한 cut/ awk기도합니다. 나는 이것을 사용하지 않는 맥에서 시도하고 csvtool있지만 맥에는 루비가 함께 제공되므로 다음과 같이 할 수 있습니다.

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

4

먼저 기본 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

3
csvtool col 2 file.csv 

여기서 2는 관심있는 열입니다.

당신은 또한 할 수 있습니다

csvtool col 1,2 file.csv 

여러 열을 수행하려면


3

가장 쉬운 방법은 csvkit을 사용하는 것입니다 .

두 번째 열을 가져옵니다. 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'


2

지금까지 csvkit을 언급하지 않은 이유가 궁금합니다.

csvkit은 CSV로 변환하고 작업하기위한 명령 줄 도구 모음입니다.

csvkit 문서

나는 그것을 csv 데이터 관리에만 독점적으로 사용하고 있으며 지금까지 cvskit을 사용하여 해결할 수없는 문제를 발견하지 못했습니다.

cvs 파일에서 하나 이상의 열을 추출하려면 csvcut도구 상자의 일부인 유틸리티를 사용할 수 있습니다 . 두 번째 열을 추출하려면 다음 명령을 사용하십시오.

csvcut -c 2 filename_in.csv > filename_out.csv 

csvcut 참조 페이지

csv의 문자열이 인용 된 경우 다음 q옵션 과 함께 인용 문자를 추가합니다 .

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

pip install csvkit또는로 설치하십시오 sudo apt install csvkit.



0

잠시 동안이 코드를 사용해 왔지만 "스택 오버플로에서 잘라 내기 및 붙여 넣기"를 계산하지 않는 한 "빠른"것은 아닙니다.

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

0

while 루프를 사용할 수도 있습니다.

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

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

이 코드는 Shellcheck 경고 : SC2034를 생성합니다 . 검색은 경고를 회피 할 방법을 찾을 때이 질문을 첫 번째 결과로 반환합니다.
jww
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.