열 이름이 CSV 파일의 특정 패턴과 일치하는 전체 열을 추출하는 방법은 무엇입니까?

열 이름이 CSV 파일의 특정 패턴과 일치하는 전체 열을 추출하는 방법은 무엇입니까?

저는 Unix에 익숙하지 않고 매우 큰 CSV 파일을 작업하고 있습니다.

예는 다음과 같습니다.

ABC1,ABC2,ABC3,DDD,EEE,FFF
1,2,3,4,5,6
1,2,3,4,5,6

로 시작하는 모든 열을 추출하는 방법은 무엇입니까 ABC?

답변1

다음 awk프로그램이 그 트릭을 수행할 것입니다. 다음과 같은 파일에 저장하십시오 extract.awk.

#!/bin/awk -f

BEGIN { FS=OFS=","}

FNR==1 {
  for (i=1;i<=NF;i++) {
    if (index($i,startstr)==1) cols[++ncol]=i;
  }
}

{ for (j=1;j<=ncol;j++) printf("%s%s",$(cols[j]),j==ncol?ORS:OFS) }

그럼 전화하면 돼

~$ awk -f extract.awk -v startstr="ABC" input.csv
ABC1,ABC2,ABC3
1,2,3
1,2,3

문자열을 찾으려는 변수의 위치를 ​​정의합니다 startstr.

그러면 먼저 입력 및 출력 필드 구분 기호가 로 설정됩니다 ,.

  • 첫 번째 행(헤더 행)에서는 변수에 저장된 검색 문자열로 시작하는 열 이름이 있는지 확인합니다 startstr. 그렇다면 열 번호가 cols"인쇄할 열" 배열에 추가됩니다.
  • 각 행(첫 번째 행 포함)에 대해 저장된 모든 열의 값을 인쇄한 cols다음 마지막 열인 경우 필드 구분 기호 또는 레코드 구분 기호(기본값은 줄 바꿈)를 인쇄합니다.

실제 검색 문자열에 정규식 컨텍스트에 특수 문자가 포함되어 있는 경우 index()우리가 사용하는 함수는 정규식 기반 일치 가 아닌 문자 그대로의 문자열 일치를 수행합니다. awk정규식 기본 검색을 사용해야 하는 경우 다음을 변경하세요.

if (index($i,startstr)==1) cols[++ncol]=i;

도착하다

if ($i ~ startstr) cols[++ncol]=i

그러나 그 안의 모든 문자는 startstr정규식 토큰으로 해석되므로 주의하지 않으면 예기치 않은 동작이 발생할 수 있습니다. 언급하신 예의 startstr경우 ^ABC.

답변2

awk를 사용하여 이 작업을 수행할 수 있지만 Perl의 어레이 슬라이싱 기능 덕분에 Perl에서는 더 쉽습니다. awk에서는 동일한 결과를 얻으려면 필요한 배열을 반복해야 합니다.

#!/usr/bin/perl

use strict;
my @wanted;   # array to hold the indices we want to print

while(<>) {
  chomp;

  # split the input line into array @F, using commas as the delimiter.
  my @F = split /,/;

  if ($. == 1) {  # process the first line (the headers)
    # if a header matches the regex, add it to @wanted
    foreach my $i (0 .. $#F) {
      push @wanted, $i if $F[$i] =~ m/^abc/i;
    };
  };

  # print the columns of @F whose indices are listed in @wanted
  print join(",", @F[@wanted]), "\n";
}

예를 들어 다른 이름으로 저장 abc.pl하고 실행 가능하게 만든 chmod +x abc.pl후 다음과 같이 실행하십시오.

$ ./abc.pl input.csv
ABC1,ABC2,ABC3
1,2,3
1,2,3

작동 원리:

  • 루프는 각 필드의 foreach일치하는 인덱스 번호 (대소문자 구분 안 함)를 배열 에 추가합니다./abc/@wanted
  • 주어진 샘플 입력 후에는 최종적으로 , 및 가 @wanted포함됩니다 .012
  • @F[@wanted]명령문에 사용된 as는 실제로 print join()(즉, 요소 ​​및 of)와 동일합니다. 이러한 요소는 쉼표 문자로 연결되어 인쇄됩니다.@F[0,1,2]012@F

추가 사항:

if ($. == 1) {...}foreachPerl의 기능을 사용하기 위해 using 블록을 다시 작성할 수 있습니다 grep. 전체 블록은 단 한 줄로 대체될 수 있습니다:

   @wanted = grep($F[$_] =~ m/^abc/i, keys @F) if ($. == 1);

어떤 사람들은 이것이 Perl 관용적이라고 말할 것입니다. 나는 동의하지 않습니다. Perl에는 foreachand grep(및 map배열 join이나 목록을 다루는 다른 많은 함수와 연산자)가 있으며 다음을 사용합니다.어느그중에는 "관용적 펄"이 있습니다.

참고: keys인덱스 배열을 사용하려면 2010년에 출시된 v5.12 이상의 Perl 버전이 필요합니다. 이전에는 keys해시 배열로만 작업했습니다.

또한 전체 스크립트는 다음 두 개의 문만 사용하여 한 줄로 압축할 수 있습니다.

$ perl -F, -lne '@wanted = grep($F[$_] =~ m/^abc/i, keys @F) if ($. == 1);
                 print join(",", @F[@wanted]);' input.csv

답변3

사용하기 매우 쉽습니다.밀러,WHO자르다정규식 일치 열 이름에 대한 옵션이 있습니다.

$ mlr --csv cut -r -f '^ABC' input.csv
ABC1,ABC2,ABC3
1,2,3
1,2,3

답변4

flds=$(< file head -n 1 | tr ',' '\n' | grep -ne '^ABC' | cut -d: -f1 | paste -sd, -)

cut -d, -f"${flds}" file

ABC1,ABC2,ABC3
1,2,3
1,2,3

이를 두 단계로 수행합니다. 먼저 헤더를 추출한 다음 헤더에서 ABC로 시작하는 필드에 대한 필드 번호를 가져옵니다.

다음으로 이 정보를 사용하여 cut 명령에 연결하여 전체 파일에서 이러한 필드를 추출합니다.

관련 정보