mycommand.sh
두 번 실행할 수없는 스크립트 가 있습니다. 출력을 두 개의 다른 파일로 나누고 싶습니다. 하나는 정규 표현식과 일치하는 줄을 포함하는 파일과 하나는 정규 표현식과 일치하지 않는 줄을 포함하는 파일입니다. 내가 갖고 싶은 것은 기본적으로 다음과 같습니다.
./mycommand.sh | grep -E 'some|very*|cool[regex].here;)' --match file1.txt --not-match file2.txt
출력을 파일로 리디렉션 한 다음 -v 옵션을 사용하거나 사용하지 않고 두 개의 다른 grep으로 리디렉션하고 출력을 두 개의 다른 파일로 리디렉션 할 수 있다는 것을 알고 있습니다. 그러나 나는 하나의 grep으로 그것을 할 수 있는지 궁금해하고있었습니다.
그래서 한 줄로 원하는 것을 얻을 수 있습니까?
답변
이를 달성하는 방법에는 여러 가지가 있습니다.
awk 사용
다음은 coolregex
file1 과 일치하는 모든 행을 보냅니다 . 다른 모든 줄은 file2로 이동합니다.
./mycommand.sh | awk '/[coolregex]/{print>"file1";next} 1' >file2
작동 방식 :
-
/[coolregex]/{print>"file1";next}
정규식
coolregex
과 일치하는 모든 행 이로 인쇄됩니다file1
. 그런 다음 나머지 모든 명령을 건너 뛰고next
줄 에서 다시 시작 합니다. -
1
다른 모든 줄은 stdout으로 전송됩니다.
1
print-the-line을위한 awk의 비밀스러운 속기입니다.
여러 스트림으로 분할하는 것도 가능합니다.
./mycommand.sh | awk '/regex1/{print>"file1"} /regex2/{print>"file2"} /regex3/{print>"file3"}'
프로세스 대체 사용
이것은 awk 솔루션만큼 우아하지는 않지만 완전성을 위해 프로세스 대체와 결합 된 여러 그렙을 사용할 수도 있습니다.
./mycommand.sh | tee >(grep 'coolregex' >File1) | grep -v 'coolregex' >File2
여러 스트림으로 나눌 수도 있습니다.
./mycommand.sh | tee >(grep 'coolregex' >File1) >(grep 'otherregex' >File3) >(grep 'anotherregex' >File4) | grep -v 'coolregex' >File2
답변
sed -n -e '/pattern_1/w file_1' -e '/pattern_2/w file_2' input.txt
w filename
-현재 패턴 공간을 파일 이름으로 씁니다.
일치하는 모든 줄을 이동하고 file_1
일치하지 않는 모든 줄을 다음 file_2
과 같이하려면 다음을 수행하십시오.
sed -n -e '/pattern/w file_1' -e '/pattern/!w file_2' input.txt
또는
sed -n '/pattern/!{p;d}; w file_1' input.txt > file_2
설명
/pattern/!{p;d};
/pattern/!
-부정-줄에 포함되지 않은 경우pattern
.p
-현재 패턴 공간을 인쇄합니다.d
-패턴 공간을 삭제합니다. 다음 사이클을 시작하십시오.- 따라서 행에 패턴이 없으면이 행을 표준 출력에 인쇄하고 다음 행을 선택합니다.
file_2
우리의 경우 표준 출력은로 리디렉션됩니다 . 줄이 패턴과 일치하지 않는 동안sed
스크립트 의 다음 부분 (w file_1
)에 도달하지 않습니다.
w file_1
-선에 패턴이 포함 된/pattern/!{p;d};
경우 패턴이 일치하지 않을 때만 실행되므로 부품이 생략되므로이 행은로 이동합니다file_1
.
답변
나는 sed
bashism에 의존하지 않고 출력 파일을 동일한 기반으로 처리하기 때문에 솔루션을 좋아했습니다 . AFAIK에는 원하는 것을 수행하는 독립 실행 형 Unix 도구가 없으므로 직접 프로그래밍해야합니다. 스위스 군용 칼 접근 방식을 포기하려면 모든 스크립팅 언어 (Perl, Python, NodeJS)를 사용할 수 있습니다.
이것이 NodeJS에서 수행되는 방법입니다
#!/usr/bin/env node
const fs = require('fs');
const {stderr, stdout, argv} = process;
const pattern = new RegExp(argv[2] || '');
const yes = argv[3] ? fs.createWriteStream(argv[3]) : stdout;
const no = argv[4] ? fs.createWriteStream(argv[4]) : stderr;
const out = [no, yes];
const partition = predicate => e => {
const didMatch = Number(!!predicate(e));
out[didMatch].write(e + '\n');
};
fs.readFileSync(process.stdin.fd)
.toString()
.split('\n')
.forEach(partition(line => line.match(pattern)));
사용법 예
# Using designated files
./mycommand.sh | partition.js pattern file1.txt file2.txt
# Using standard output streams
./partition.js pattern > file1.txt 2> file2.txt
답변
파이썬과 다른 정규 표현식 구문을 사용하지 않는다면 :
#!/usr/bin/env python3
import sys, re
regex, os1, os2 = sys.argv[1:]
regex = re.compile(regex)
with open(os1, 'w') as os1, open(os2, 'w') as os2:
os = (os1, os2)
for line in sys.stdin:
end = len(line) - line.endswith('\n')
os[regex.search(line, 0, end) is not None].write(line)
용법
./match-split.py PATTERN FILE-MATCH FILE-NOMATCH
예
printf '%s\n' foo bar baz | python3 match-split.py '^b' b.txt not-b.txt