다른 파일에 ID가 나열된 텍스트 파일에서 행을 선택하십시오. ids.csvID의 목록 파일을 만에서 레코드를

유닉스 쉘에서 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아니라고 생각 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

답변

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

답변

이 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옵션은 첫 번째 및 두 번째 입력 파일에서 각각 결합 할 필드를 지정합니다.


답변

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

ruby -pe 'File.open("id.csv").each { |i| puts i if i =~ /\$\_/ }' table.csv