다른 파일에 ID가 나열된 텍스트 파일에서 행을 선택하십시오.


13

유닉스 쉘에서 grep awk 정렬을 많이 사용하여 중간 크기 (약 10M-100M 줄) 탭으로 구분 된 열 텍스트 파일로 작업합니다. 이와 관련하여 유닉스 쉘은 내 스프레드 시트입니다.

그러나 ID 목록이 주어진 레코드를 선택하는 것이 큰 문제입니다.

갖는 table.csv형식의 파일 id\tfoo\tbar...ids.csvID의 목록 파일을 만에서 레코드를 선택 table.csv의 ID를 사용하는 상태 ids.csv.

의 종류 /programming/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids 하지만, 쉘,하지 펄.

grep -FID가 가변 너비 인 경우 분명히 오 탐지를 생성합니다. join내가 알아낼 수없는 유틸리티입니다. 우선, 알파벳순 정렬이 필요합니다 (내 파일은 일반적으로 숫자로 정렬되어 있음). 그러나 잘못된 순서에 대해 불평하고 일부 레코드를 건너 뛰지 않으면 작동하지 않을 수 있습니다. 그래서 나는 그것을 좋아하지 않습니다. ^id\tID 수가 많은 경우 -s가있는 파일에 대한 grep -f 는 매우 느립니다. awk번거 롭습니다.

이에 대한 좋은 해결책이 있습니까? 탭으로 구분 된 파일을위한 특정 도구가 있습니까? 추가 기능도 가장 환영받을 것입니다.

UPD : 수정 sort->join


경우 grep -f그 가치보다 더 많은 문제처럼이 전략의 소리를 유지, 너무 느립니다 - 변화 가능성 같은 O (N * M) 성능 문제에 먹이를 떨어질 것이다. 아마 당신의 시간은 정규화 된 SQL DB 를 사용하는 방법을 배우는데 더 나은 시간을 보낼 것입니다 ...
goldilocks

1
링크 한 질문에서 Perl 스크립트를 사용하지 않으시겠습니까? 또는에서 비슷한 스크립트를 작성할 수 있어야합니다 awk.
cjm

Bash 4에는 연관 배열이 있는데, 이것은 중첩 루프를 perl 예제로 우회하는 데 필요한 것입니다.
goldilocks

1
sort모든 종류의 정렬, 숫자, 알파벳 및 기타를 수행 할 수 있습니다. 참조하십시오 man sort.
terdon

여기에 쿼리가 있습니다. 데이터를 추출하려는 소스 파일이 구분되지 않은 파일 인 경우 어떻게합니까?

답변:


19

나는 당신이 grep -f아니라고 생각 grep -F하지만 실제로는 둘 다의 조합이 필요합니다 -w.

grep -Fwf ids.csv table.csv

당신이 오 탐지를 얻은 이유는 (나는 당신이 설명하지 않았다고 생각합니다) id가 다른 것에 포함될 수 있다면 둘 다 인쇄되기 때문입니다. -w이 문제를 제거하고 -F패턴이 정규식이 아닌 문자열로 취급되도록합니다. 보낸 사람 man grep:

   -F, --fixed-strings
          Interpret PATTERN as a  list  of  fixed  strings,  separated  by
          newlines,  any  of  which is to be matched.  (-F is specified by
          POSIX.)
   -w, --word-regexp
          Select  only  those  lines  containing  matches  that form whole
          words.  The test is that the matching substring must  either  be
          at  the  beginning  of  the  line,  or  preceded  by  a non-word
          constituent character.  Similarly, it must be either at the  end
          of  the  line  or  followed by a non-word constituent character.
          Word-constituent  characters  are  letters,  digits,   and   the
          underscore.

   -f FILE, --file=FILE
          Obtain  patterns  from  FILE,  one  per  line.   The  empty file
          contains zero patterns, and therefore matches nothing.   (-f  is
          specified by POSIX.)

ID가 아닌 필드에 ID가 존재할 수 있기 때문에 오 탐지 인 경우 대신 파일을 반복하십시오.

while read pat; do grep -w "^$pat" table.csv; done < ids.csv

또는 더 빠름 :

xargs -I {} grep "^{}" table.csv < ids.csv

개인적으로, 나는 이것을 할 것입니다 perl:

perl -lane 'BEGIN{open(A,"ids.csv"); while(<A>){chomp; $k{$_}++}} 
            print $_ if defined($k{$F[0]}); ' table.csv

1
+1 그러나 ID 열이 아닌 ID와 정확히 일치하는 잘못된 오 탐지가있는 경우 어떻게해야합니까? ^-F와 함께 사용할 수없는 경우 첫 번째 열을 구체적으로 타겟팅 할 수 없습니다.
goldilocks

@goldilocks는 정확하게 일치하면 오 탐지가 아닙니다. 의미하는 바를 알지만이 경우 OP는 입력 파일을 표시해야합니다.
terdon

^id\tOP 의 비트 id는 다른 열에서 발생할 수 있음을 나타냅니다. 그렇지 않다면 이것은 중요하지 않습니다.
goldilocks

@goldilocks 페어 포인트, 답변 편집.
terdon

우리가 이것을 사용했던 방법은 우리가 검색하고자하는 필드를 한정하는 독특한 문자 (예 : control-A)를 추가 한 임시 파일 (awk 또는 sed를 사용하여)을 만든 다음 grep -F -f temppatternfile temptargetfile | 그럴 -d '\ 001'
마크 Plotnick

7

join유틸리티는 당신이 원하는 것입니다. 입력 파일을 사전 순으로 정렬해야합니다.

쉘이 bash 또는 ksh라고 가정하십시오.

join -t $'\t' <(sort ids.csv) <(sort table.csv)

정렬 할 필요없이 일반적인 awk 솔루션은

awk -F '\t' 'NR==FNR {id[$1]; next} $1 in id' ids.csv table.csv

내가 시도했지만 궁극적으로 전달하지 못했을 때, 조인은 위기입니다. 나를 위해 잘 작동하지 않습니다.
alamar

1
join당신의 말은 당신이 그것을 알아낼 수 없었습니다. 마음을 열고 배우십시오. 어떤 결과를 얻었습니까? 예상과 어떻게 다른가요?
glenn jackman

+1,이 직무입니다 join.
don_crissti

awk솔루션은 나의 목적을 위해 매우 빠르고 효율적입니다 (~ 100M 줄의 파일에서 수백 개의 하위 세트를 추출하고 있습니다)
Luke

2

이 SO 질문에 대한 답변 은 조인을 통해 문제를 해결 하는 데 도움 되었습니다. 기본적으로 파일을 결합하기 위해 파일을 정렬 할 때 결합 할 열을 기준으로 정렬해야합니다. 따라서 이것이 첫 번째 문자 인 경우 파일에 구분 문자가 무엇인지 알려주고 첫 번째 필드 (및 첫 번째 필드에서만)를 정렬해야한다는 것을 알려 주어야합니다. 그렇지 않으면 첫 번째 필드의 너비가 가변적 인 경우 구분 기호 및 다른 필드가 정렬 순서에 영향을 줄 수 있습니다.

따라서 sort의 -t 옵션을 사용하여 분리 문자를 지정하고 -k 옵션을 사용하여 필드를 지정하십시오 (시작 및 끝 필드가 동일하더라도 같은지 또는 해당 문자에서 정렬됨을 기억하십시오) 줄 끝까지).

따라서이 질문과 같이 탭으로 구분 된 파일 경우 구조에 대한 glenn의 답변 덕분에 다음이 작동합니다 .

join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv

(-d 플래그는 사전 정렬을 의미합니다. -b 플래그를 사용하여 선행 공백을 무시할 수도 있습니다 ( man sort및 참조 man join).

보다 일반적인 예로, input1.csv세 번째 열과 input2.csv네 번째 열에 쉼표로 구분 된 두 파일을 결합한다고 가정합니다 . 당신은 사용할 수 있습니다

join -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv

여기에서 -1-2옵션은 첫 번째 및 두 번째 입력 파일에서 각각 결합 할 필드를 지정합니다.


0

루비를 사용하여 비슷한 것을 할 수도 있습니다.

ruby -pe 'File.open("id.csv").each { |i| puts i if i =~ /\$\_/ }' table.csv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.