두 개의 파일이 있는데 파일 1의 단어가 파일 2에 없으면 새 파일(예: 파일 3)의 해당 줄에 false라는 단어를 생성하고 싶습니다. 그렇지 않으면 해당 줄에 true를 출력하고 싶습니다.
파일 1:
a
b
c
d
파일 2:
a
d
c
e
t
y
파일 3:
true
false
true
true
awk/sed/grep 명령을 사용하여 이를 수행할 수 있는 방법이 있습니까?
답변1
file2가 비어 있지 않고 문제를 일으키지 않고 메모리에 들어갈 만큼 크지 않다고 가정합니다.
awk 'NR==FNR{a[$0]; next} {print ($0 in a ? "true" : "false")}' file2 file1
답변2
man grep bash
UNTESTED와 같은 작업을 읽고 수행합니다.
for pat in $(cat "file 1") ; do
ans="False"
grep --quiet "^$pat\$" "file 2" || \
ans="True"
echo -e "$pat\t$ans" >>"file 3"
답변3
$ perl -le '
# construct a partial regex from the first filename argument
my $re = join("|", split /\n+/, do { local(@ARGV,$/) = shift; <> });
# complete and pre-compile the regex
$re = qr/\b(?:$re)\b/;
# read and process stdin and/or remaining filename args
while(<>) {
print /$re/ ? "true" : "false"
}' file2 file1
true
false
true
true
이 Perl 스크립트는 첫 번째 인수( )가 나타내는 전체 파일을 읽고 file2
파일의 각 줄에 있는 각 단어와 일치하는 정규식을 구성합니다. 정규식의 각 단어는 교대 문자로 구분됩니다 |
. 파일에는 한 줄에 하나의 단어가 포함되어 있고 줄은 하나 이상의 줄 바꿈으로 구분되어 있다고 가정합니다(이것은 빈 줄을 무시하는 유용한 부작용이 있습니다).
참고: 의 각 단어는 file2
정규식으로 해석됩니다. 고정 문자열로 해석되도록 하려면 행을 my $re ...
다음과 같이 변경하십시오.
my $re = join("|", map { quotemeta $_ } split /\n+/, do { local(@ARGV,$/) = shift; <> });
quotemeta 함수는 문자열의 모든 정규식 메타 문자를 "인용"하여 특별한 의미를 잃고 리터럴 문자로 처리되도록 합니다. 바라보다 perldoc -f quotemeta
. 이 함수는 반환된 목록의 각 요소에 블록이 적용되도록 map
합니다 . 및 을 참조하십시오 .{ quotemeta $_ }
split
perldoc -f map
perldoc -f split
덧붙여서, do { local(@ARGV,$/) = shift; <> })
이것은 파일을 "slurping"하는 데 사용되는 매우 일반적인 Perl 관용어입니다. 즉, 전체 파일을 한 번에 읽는 것입니다. 다음을 포함하여 동일한 작업을 수행하는 다른 방법이 많이 있습니다.파일::흡연자, 그러나 이는 간단하고 이식 가능하며 라이브러리 모듈을 사용하거나 설치할 필요가 없습니다.
그런 다음 스크립트는 qr
참조 연산자를 사용하여 정규식을 미리 컴파일하여 성능을 향상시킵니다. 각 루프에서 동일한 정규식을 다시 컴파일하면 CPU 시간이 크게 낭비됩니다. \b
부분 일치를 방지하고 일치 항목을 잡는 것을 방지하는 데 사용되는 단어 경계 표시자 ?:
(일치 항목이 발생했다는 사실만 감지하면 되고 일치 항목을 어떤 용도로도 사용할 필요가 없기 때문에 이는 시간 낭비일 뿐입니다). 참조 perldoc -f qr
및man perlre
정규식은 대소문자를 구분하지만 정규식 i
수정자를 사용하여 대소문자를 구분하지 않게 만들 수 있습니다.
$re = qr/\b(?:$re)\b/i;
그런 다음 스크립트는 나머지 입력을 읽고 각 입력 줄에 대해 해당 줄이 정규식과 일치하면 "true"를 인쇄하고, 그렇지 않으면 "false"를 인쇄합니다. 이를 사용하기 때문에 while(<>)
표준 입력 및/또는 파일 이름 인수에서 데이터를 읽습니다. 이 예에서는 에서 입력을 읽습니다 file1
.
메모리 사용량은 첫 번째 파일의 크기에 비례합니다. 단어가 많을수록 file2
더 많은 RAM을 사용합니다. 물론 실행 시간은 첫 번째 파일의 크기와 기타 모든 입력에 비례합니다.
답변4
tmp=$(mktemp)
comm -2 <(sort file1) <(sort file2) \
| sed -e 's/^\t.*/true/;t
c false' > "$tmp"
paste <(cat -n < "$tmp") \
<(cat -n file1 | sort -bk2) \
| sort -bk3,3n | cut -f2
산출:-
true
false
true
true
노트:
- 첫 번째 단계로 두 파일을 정렬하고 comm을 통해 실행하고 두 번째 파일을 억제합니다.
- 다음 sed는 true/false 요소를 식별합니다.
- 마지막으로 원래 순서를 복원하기 위해 출력과 숫자로 정렬된 입력을 붙여넣습니다.