유닉스 쉘에서 grep awk 정렬을 많이 사용하여 중간 크기 (약 10M-100M 줄) 탭으로 구분 된 열 텍스트 파일로 작업합니다. 이와 관련하여 유닉스 쉘은 내 스프레드 시트입니다.
그러나 ID 목록이 주어진 레코드를 선택하는 것이 큰 문제입니다.
갖는 table.csv
형식의 파일 id\tfoo\tbar...
및 ids.csv
ID의 목록 파일을 만에서 레코드를 선택 table.csv
의 ID를 사용하는 상태 ids.csv
.
의 종류 /programming/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids 하지만, 쉘,하지 펄.
grep -F
ID가 가변 너비 인 경우 분명히 오 탐지를 생성합니다.
join
내가 알아낼 수없는 유틸리티입니다. 우선, 알파벳순 정렬이 필요합니다 (내 파일은 일반적으로 숫자로 정렬되어 있음). 그러나 잘못된 순서에 대해 불평하고 일부 레코드를 건너 뛰지 않으면 작동하지 않을 수 있습니다. 그래서 나는 그것을 좋아하지 않습니다. ^id\t
ID 수가 많은 경우 -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