grep을 사용하여 출력을 두 파일로 분할하는 방법은 무엇입니까?


14

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

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

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

그래서 한 줄로 원하는 것을 얻을 수 있습니까?

답변:


20

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

awk 사용

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

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

작동 방식 :

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

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

  2. 1

    다른 모든 줄은 stdout으로 전송됩니다. 1print-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

오 쿨! file2 대신 다른 awk를 수행하지 않고 여러 파일로 분할 할 수도 있습니까? 예를 들어 정규 표현식이 겹칠 수 있다는 의미입니다.
yukashima huksay

1
@ aran 예, awk는 매우 유연합니다. 정확히 어떻게 수행되는지는 정규 표현식이 어떻게 겹치는 지에 달려 있습니다.
John1024

중복 정규 표현식을 지원하지 않더라도 솔루션을보고 싶습니다. 겹쳐서 나는 부분 집합의 교차가 신경없이 비워지지 않는 것을 의미합니다.
yukashima huksay 2012

1
@ aran 두 가지 방법 모두에 대해 여러 스트림으로 답변 예제에 추가했습니다.
John1024

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

마지막 솔루션에 대한 설명을 추가해 주시겠습니까?
yukashima huksay

@aran 설명이 추가되었습니다. 또한 명령이 수정됩니다 - file_1file_2올바른 순서로 교체되었다.
MiniMax

0

나는 sedbashism에 의존하지 않고 출력 파일을 동일한 기반으로 처리하기 때문에 솔루션을 좋아했습니다 . 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

0

파이썬과 다른 정규 표현식 구문을 사용하지 않는다면 :

#!/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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.