grep을 사용하여 출력을 두 파일로 분할하는 방법은 무엇입니까? 스크립트 가 있습니다. 출력을 두 개의

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

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

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

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



답변

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

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


답변

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.

답변

나는 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


답변

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

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


답변