이름별로 특정 열을 인쇄하는 방법은 무엇입니까?


32

다음 파일이 있습니다.

id  name  age
1   ed    50
2   joe   70   

idage열만 인쇄하고 싶습니다 . 지금은 그냥 사용합니다 awk:

cat file.tsv | awk '{ print $1, $3 }'

그러나 열 번호를 알아야합니다. 열 번호 대신 열 이름 (첫 번째 행에 지정)을 사용할 수있는 방법이 있습니까?


7
cat필요하지 않습니다, BTW. 당신은 사용할 수 있습니다awk '{ print $1, $3 }' file.tsv
에릭 윌슨

그렇지 않으면 열 수 , 다음 무엇을 당신에 의존 하시겠습니까?
rozcietrzewiacz

2
@rozcietrzewiacz 이름; 그가 말하고자하는 id대신에 $1하고 age대신$3
마이클 Mrozek

답변:


37

아마도 이런 식으로 뭔가 :

$ cat t.awk
NR==1 {
    for (i=1; i<=NF; i++) {
        ix[$i] = i
    }
}
NR>1 {
    print $ix[c1], $ix[c2]
}
$ awk -f t.awk c1=id c2=name input 
1 ed
2 joe
$ awk -f t.awk c1=age c2=name input 
50 ed
70 joe

명령 행에 인쇄 할 열을 지정하려면 다음과 같이하십시오.

$ cat t.awk 
BEGIN {
    split(cols,out,",")
}
NR==1 {
    for (i=1; i<=NF; i++)
        ix[$i] = i
}
NR>1 {
    for (i in out)
        printf "%s%s", $ix[out[i]], OFS
    print ""
}
$ awk -f t.awk -v cols=name,age,id,name,id input 
ed 1 ed 50 1 
joe 2 joe 70 2 

( 블록에 -v정의 된 변수를 가져 오려면 스위치를 참고하십시오 BEGIN.)


나는 awk 학습을 미루어 냈습니다 ... 가변 열을 지원하는 가장 좋은 방법은 무엇입니까? awk -f t.awk col1 col2 ... coln input이상적 일 것입니다. awk -f t.awk cols=col1,col2,...,coln input너무 일 것이다
브렛 토마스에게

1
내 답변을 업데이트했습니다. 당신이 그것으로 물건을하고 싶다면 그것을 배우고 그만 두십시오 :)
매트

3
두 번째 예는 열을 예상 순서대로 출력하지 않으며 for (i in out)고유 한 순서가 없습니다. 솔루션으로 gawk제공 PROCINFO["sorted_in"]하여 인덱스를 반복하는 for( ; ; )것이 더 좋습니다.
mr.spuratic

@BrettThomas는 이 튜토리얼을 적극 권장 합니다 . (lynda.com에 액세스 할 수 있다면 "Awk 필수 교육"을 강력히 권장합니다. "Awk 필수 교육"은 동일한 자료를 모두 간결하고 연습 연습으로 다루는 것입니다.
Wildcard

스 퍼러 틱 씨, 다맨 나는 for (i in out) 문제를 가로 질러 3 필드에서 잘 작동했습니다 .2를 추가하면 예상했던 1,2,3,4,5 대신 4,5,1,2,3이되었습니다 . 그것들을 순서대로 얻으려면 (i = 1; i <= length (out); i ++)
Severun

5

Perl 솔루션을 로트에 넣는 것만으로도 충분합니다.

#!/usr/bin/perl -wnla

BEGIN {
    @f = ('id', 'age');   # field names to print
    print "@f";           # print field names
}

if ($. == 1) {            # if line number 1
    @n = @F;              #   get all field names
} else {                  # or else
    @v{@n} = @F;          #   map field names to values
    print "@v{@f}";       #   print values based on names
}

5

csvkit

입력 데이터를 CSV 형식으로 변환하고 다음과 같은 CSV 도구를 사용 csvcut하십시오 csvkit.

$ cat test-cols.dat 
id  name  age
1   ed    50
2   joe   70 

csvkit을 설치하십시오.

$ pip install csvkit

trsqueeze 옵션과 함께 사용 -s하여 유효한 csv 파일로 변환하고 적용하십시오 csvcut.

$ cat test-cols.dat | tr -s ' ' ',' | csvcut -c id,age
id,age
1,50
2,70

이전 데이터 형식으로 돌아가려면 다음을 사용할 수 있습니다. tr ',' ' ' | column -t

$ cat test-cols.dat | tr -s ' ' ',' | csvcut -c id,age | tr ',' ' ' | column -t
id  age
1   50
2   70

노트

  • csvkit은 다른 구분 기호 ( 공유 옵션 -d 또는 --delimiter) 도 지원 하지만 csv 파일을 반환합니다.

    • 파일이 공백을 사용하여 열을 분리하면 (탭이 전혀 없음) 다음 작업이 수행됩니다.

      $ csvcut -d ' ' -S -c 'id,age' test-cols.dat
      id,age
      1,50
      2,70
    • 파일이 탭을 사용하여 열을 분리하면 다음 작업이 수행되고 csvformattsv 파일을 다시 얻는 데 사용할 수 있습니다.

      $ csvcut -t -c 'id,age' test-cols.dat | csvformat -T
      id  age
      1   50
      2   70

      내가 확인한 한 단일 탭만 허용됩니다.

  • csvlook 마크 다운 테이블 형식으로 테이블을 형식화 할 수 있습니다.

    $ csvcut -t -c "id,age" test-cols.dat | csvlook
    | id | age |
    | -- | --- |
    |  1 |  50 |
    |  2 |  70 |
  • UUOC (Unless Use of Cat) : 명령을 구성하는 것이 좋습니다.


+1. 그러나의 불필요한 사용 tr도 있습니다. TSV 파일은 CSV로 변환 할 필요없이 직접 지원됩니다. -t(일명 --tabs) 옵션을 알려줍니다 cvscut필드 구분 기호로 탭을 사용 할 수 있습니다. 그리고 -d또는 --delimiter구분 기호로 모든 문자를 사용할 수 있습니다.
cas

몇 가지 테스트와 함께, 그것은 보인다 -d-t옵션은 반 깨진입니다. 입력 구분 기호를 지정하기 위해 작동하지만 출력 구분 기호는 항상 쉼표로 하드 코딩됩니다. 손상된 IMO-입력 구분 기호와 동일하거나 사용자가 awkFS 및 OFS vars와 같이 출력 구분 기호를 설정할 수있는 다른 옵션이 있어야 합니다.
cas

4

숫자 대신 이름 으로 해당 필드를 참조하려는 경우 다음 을 사용할 수 있습니다 read.

while read id name age
do
  echo "$id $age"
done < file.tsv 

편집하다

나는 마침내 당신의 의미를 보았다! 다음은 명령 행에서 지정한 열만 이름으로 인쇄하는 bash 함수입니다 .

printColumns () 
{ 
read names
while read $names; do
    for col in $*
    do
        eval "printf '%s ' \$$col"
    done
    echo
done
}

제시 한 파일과 함께 사용하는 방법은 다음과 같습니다.

$ < file.tsv printColumns id name
1 ed 
2 joe 

(이 기능은 판독 stdin. < file.tsv printColumns ... 의 상당 printColumns ... < file.tsv하고 cat file.tsv | printColumns ...)

$ < file.tsv printColumns name age
ed 50 
joe 70 

$ < file.tsv printColumns name age id name name name
ed 50 1 ed ed ed 
joe 70 2 joe joe joe

참고 : 요청한 열 이름에주의하십시오! 이 버전은 온 전성 검사가 없으므로 인수 중 하나가 다음과 같은 경우 불쾌한 일이 발생할 수 있습니다."anything; rm /my/precious/file"


1
또한 열 번호를 알아야합니다. 당신이 그 이름을 그냥 있기 때문에 id, name그리고 age, 순서가 하드 코딩된다는 사실은 변경되지 않습니다 read라인.
janmoesen

1
예 @janmoesen, 드디어 포인트를 :) 있어요
rozcietrzewiacz

감사합니다. 큰 파일 (1000 열, 수백만 행)로 작업하고 있으므로 속도를 위해 awk를 사용하고 있습니다.
Brett Thomas

@BrettThomas 아, 알겠습니다. 나는 매우 궁금합니다 : 시간 비교를 제공하는 벤치 마크를 게시 할 수 있습니까? (사용 time { command(s); }).
rozcietrzewiacz

@rozceitrewaicz :time cat temp.txt | ./col1 CHR POS > /dev/null 99.144u 38.966s 2:19.27 99.1% 0+0k 0+0io 0pf+0w time awk -f col2 c1=CHR c2=POS temp.txt > /dev/null 0.294u 0.127s 0:00.50 82.0% 0+0k 0+0io 0pf+0w
Brett Thomas

3

그만한 가치가 있습니다. 이것은 선택한 출력 순서에 관계없이 소스에있는 열과 인쇄 할 열을 처리 할 수 ​​있습니다. 그냥 인수를 다시 정렬하십시오 ...

예. 요구:script-name id age

outseq=($@)
colnum=($( 
  for ((i; i<${#outseq[@]}; i++)) ;do 
    head -n 1 file |
     sed -r 's/ +/\n/g' |
      sed -nr "/^${outseq[$i]}$/="
  done ))
tr ' ' '\t' <<<"${outseq[@]}"
sed -nr '1!{s/ +/\t/gp}' file |
  cut -f $(tr ' ' ','<<<"${colnum[@]}") 

산출

id      age
1       50
2       70

2

파일이 당신이 읽고있는이 경우 가능성이 결코 수있는 사용자 생성 할 수 없습니다, 당신은 내장 읽기를 악용 할 수 있습니다 :

f=file.tsv
read $(head -n1 "$f") extra <<<`seq 100`
awk "{print \$$id, \$$age}" "$f"

입력 파일의 첫 번째 전체 행이 인수 목록으로 대체되므로 read헤더 행의 모든 ​​필드 이름이 변수 이름으로 전달됩니다. 이 중 첫 번째는 seq 100생성 하는 1을 할당 받고 두 번째는 2를, 세 번째는 3을 얻습니다. 초과 seq출력은 더미 변수에 의해 흡수됩니다 extra. 입력 열의 수를 미리 알고 있으면 100을 변경하여 제거 할 수 있습니다 extra.

awk스크립트에 의해 정의 된 쉘 변수 수, 이중 인용 문자열 read로 스크립트로 대체 될 awk필드 번호를.


1

일반적으로 파일 헤더를보고 필요한 열 수를 계산 한 다음 ( c ) Unix를 사용 하는 것이 더 쉽습니다 cut.

cut -f c -d, file.csv

그러나 많은 열이나 많은 파일이 있으면 다음과 같은 추한 트릭을 사용합니다.

cut \
  -f $(head -1 file.csv | sed 's/,/\'$'\n/g' | grep -n 'column name' | cut -f1 -d,) \
  -d, \ 
  file.csv

OSX에서 테스트 한 file.csv쉼표로 구분됩니다.


1

단일 열을 선택하는 빠른 방법이 있습니다.

"foo"라는 열을 원한다고 가정 해 봅시다.

f=file.csv; colnum=`head -1 ${f} | sed 's/,/\n/g' | nl | grep 'foo$' | cut -f 1 `; cut -d, -f ${colnum} ${f}

기본적으로 헤더 행을 가져 와서 행당 하나의 열 이름을 가진 여러 행으로 분할하고 행 번호를 지정하고 원하는 이름의 행을 선택한 후 연관된 행 번호를 검색하십시오. 그런 다음 해당 줄 번호를 잘라 내기 명령의 열 번호로 사용하십시오.


0

비슷한 솔루션을 찾고 있는데 (열 번호가 다양 할 수있는 id라는 열이 필요합니다), 나는 이것을 발견했습니다.

head -n 1 file.csv | awk -F',' ' {
      for(i=1;i < NF;i++) {
         if($i ~ /id/) { print i }
      }
} '

0

이 목적을 위해 기본적으로 다음과 같이 작동하는 Python 스크립트를 작성했습니다.

with fileinput.input(args.file) as data:
    headers = data.readline().split()
    selectors = [any(string in header for string in args.fixed_strings) or
                 any(re.search(pat, header) for pat in args.python_regexp)
                 for header in headers]

    print(*itertools.compress(headers, selectors))
    for line in data:
        print(*itertools.compress(line.split(), selectors))

나는 그것을 호출 hgrep하기위한 헤더 그렙 이 다음과 같이 사용할 수 있습니다 :

$ hgrep data.txt -F foo bar -P ^baz$
$ hgrep -F foo bar -P ^baz$ -- data.txt
$ grep -v spam data.txt | hgrep -F foo bar -P ^baz$

전체 스크립트는 argparse명령 행 인수를 구문 분석 하는 데 사용 되며 코드는 다음과 같습니다.

#!/usr/bin/python3

import argparse
import fileinput
import itertools
import re
import sys
import textwrap


def underline(s):
    return '\033[4m{}\033[0m'.format(s)


parser = argparse.ArgumentParser(
    usage='%(prog)s [OPTIONS] {} [FILE]'.format(
        underline('column-specification')),
    description=
        'Print selected columns by specifying patterns to match the headers.',
    epilog=textwrap.dedent('''\
    examples:
      $ %(prog)s data.txt -F foo bar -P ^baz$
      $ %(prog)s -F foo bar -P ^baz$ -- data.txt
      $ grep -v spam data.txt | %(prog)s -F foo bar -P ^baz$
    '''),
    formatter_class=argparse.RawTextHelpFormatter,
)

parser.add_argument(
    '-d', '--debug', action='store_true', help='include debugging information')
parser.add_argument(
    'file', metavar='FILE', nargs='?', default='-',
    help="use %(metavar)s as input, default is '-' for standard input")
spec = parser.add_argument_group(
    'column specification', 'one of these or both must be provided:')
spec.add_argument(
    '-F', '--fixed-strings', metavar='STRING', nargs='*', default=[],
    help='show columns containing %(metavar)s in header\n\n')
spec.add_argument(
    '-P', '--python-regexp', metavar='PATTERN', nargs='*', default=[],
    help='show a column if its header matches any %(metavar)s')

args = parser.parse_args()

if args.debug:
    for k, v in sorted(vars(args).items()):
        print('{}: debug: {:>15}: {}'.format(parser.prog, k, v),
              file=sys.stderr)

if not args.fixed_strings and not args.python_regexp:
    parser.error('no column specifications given')


try:
    with fileinput.input(args.file) as data:
        headers = data.readline().split()
        selectors = [any(string in header for string in args.fixed_strings) or
                     any(re.search(pat, header) for pat in args.python_regexp)
                     for header in headers]

        print(*itertools.compress(headers, selectors))
        for line in data:
            print(*itertools.compress(line.split(), selectors))

except BrokenPipeError:
    sys.exit(1)
except KeyboardInterrupt:
    print()
    sys.exit(1)


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.