grep을 사용하여 출력을 두 개의 파일로 어떻게 분할할 수 있습니까?

grep을 사용하여 출력을 두 개의 파일로 어떻게 분할할 수 있습니까?

mycommand.sh두 번 실행할 수 없는 스크립트가 있습니다. 출력을 두 개의 다른 파일로 분할하고 싶습니다. 하나는 정규식과 일치하는 줄을 포함하고 다른 하나는 정규식과 일치하지 않는 줄을 포함합니다. 내가 원하는 것은 기본적으로 다음과 같습니다.

./mycommand.sh | grep -E 'some|very*|cool[regex].here;)' --match file1.txt --not-match file2.txt

출력을 파일로 리디렉션한 다음 -v 옵션을 사용하거나 사용하지 않고 두 개의 다른 grep으로 리디렉션하고 해당 출력을 두 개의 다른 파일로 리디렉션할 수 있다는 것을 알고 있습니다. 하지만 grep으로 할 수 있는지 알고 싶습니다.

그렇다면 한 줄로 내가 원하는 것을 이룰 수 있을까?

답변1

이를 달성하는 방법에는 여러 가지가 있습니다.

awk를 사용하세요

다음 명령은 coolregexfile1과 일치하는 모든 행을 보냅니다. 다른 모든 줄은 file2로 이동합니다.

./mycommand.sh | awk '/[coolregex]/{print>"file1";next} 1' >file2

작동 방식:

  1. /[coolregex]/{print>"file1";next}

    정규식과 일치하는 모든 줄은 coolregex에 인쇄됩니다 file1. 그런 다음 나머지 모든 명령을 건너뛰고 다시 시작합니다 next.

  2. 1

    다른 모든 행은 표준 출력으로 전송됩니다. 1awk의 print-the-line에 대한 신비한 약어입니다.

또한 여러 스트림으로 분할될 수도 있습니다.

./mycommand.sh | awk '/regex1/{print>"file1"} /regex2/{print>"file2"} /regex3/{print>"file3"}'

프로세스 교체 사용

이는 awk 솔루션만큼 우아하지는 않지만 완전성을 위해 프로세스 대체와 결합된 여러 grep을 사용할 수도 있습니다.

./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

답변2

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

설명하다

  1. /pattern/!{p;d};
    • /pattern/!- 음수 - 행에 pattern.
    • p- 현재 패턴 공간을 인쇄합니다.
    • d- 패턴 공간을 삭제합니다. 다음 사이클을 시작하세요.
    • 따라서 행에 패턴이 없으면 이 행이 표준 출력으로 인쇄되고 다음 행이 선택됩니다. 우리의 경우 표준 출력은 file_2. 행이 패턴과 일치하지 않으면 스크립트의 다음 부분 sed( )에 도달하지 않습니다.w file_1
  2. w file_1- 라인에 패턴이 포함되어 있으면 해당 /pattern/!{p;d};부분을 건너뛰므로(패턴이 일치하지 않는 경우에만 실행되므로) 해당 라인은 로 이동합니다 file_1.

답변3

저는 이 sed솔루션이 bashism에 의존하지 않고 동일한 기반으로 출력 파일을 처리하기 때문에 좋아합니다. AFAIK, 원하는 작업을 수행하는 독립형 Unix 도구가 없으므로 직접 프로그래밍해야 합니다. Swiss Army Knife 접근 방식을 포기하면 모든 스크립팅 언어(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

답변4

Python과 다른 정규식 구문을 사용해도 괜찮다면 다음을 수행하세요.

#!/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

관련 정보