Bagaimana cara membagi output menjadi dua file dengan grep?


14

Saya memiliki skrip mycommand.shyang tidak dapat saya jalankan dua kali. Saya ingin membagi output menjadi dua file yang berbeda satu file berisi baris yang cocok dengan regex dan satu file yang berisi baris yang tidak cocok dengan regex. Yang ingin saya miliki pada dasarnya adalah sesuatu seperti ini:

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

Saya tahu saya hanya bisa mengarahkan output ke file dan kemudian ke dua greps yang berbeda dengan dan tanpa opsi -v dan mengarahkan output mereka ke dua file yang berbeda. Tapi saya hanya bertanya-tanya apakah mungkin melakukannya dengan satu grep.

Jadi, apakah mungkin untuk mencapai apa yang saya inginkan dalam satu baris?

Jawaban:


20

Ada banyak cara untuk mencapai ini.

Menggunakan awk

Berikut ini mengirimkan baris yang cocok coolregexke file1. Semua baris lain buka file2:

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

Bagaimana itu bekerja:

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

    Garis apa pun yang cocok dengan ekspresi reguler coolregexakan dicetak file1. Lalu, kita lewati semua perintah yang tersisa dan lompat untuk memulai dari awal next.

  2. 1

    Semua baris lainnya dikirim ke stdout. 1adalah singkatan samar dari cryptic untuk print-the-line.

Membagi menjadi beberapa aliran juga dimungkinkan:

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

Menggunakan proses substitusi

Ini tidak seanggun solusi awk tetapi, untuk kelengkapan, kami juga dapat menggunakan beberapa grep yang dikombinasikan dengan penggantian proses:

./mycommand.sh | tee >(grep 'coolregex' >File1) | grep -v 'coolregex' >File2

Kami juga dapat membagi menjadi beberapa aliran:

./mycommand.sh | tee >(grep 'coolregex' >File1) >(grep 'otherregex' >File3) >(grep 'anotherregex' >File4) | grep -v 'coolregex' >File2

Oh keren! Apakah mungkin untuk membaginya ke beberapa file tanpa hanya melakukan awk lain, bukan file2? Maksud saya dengan cara yang regex bisa tumpang tindih misalnya.
yukashima huksay

1
@aran Ya, awk sangat fleksibel. Tepatnya bagaimana seseorang melakukannya tergantung pada bagaimana regex tumpang tindih.
John1024

Saya akan senang melihat solusi bahkan jika itu tidak mendukung regex yang tumpang tindih. dengan tumpang tindih yang saya maksud seperti memiliki persimpangan subset tidak kosong kosong.
yukashima huksay

1
@aran saya telah menambahkan contoh jawaban dengan beberapa aliran untuk kedua metode.
John1024

8
sed -n -e '/pattern_1/w file_1' -e '/pattern_2/w file_2' input.txt

w filename - tulis ruang pola saat ini ke nama file.

Jika Anda ingin semua baris yang cocok masuk file_1dan semua baris yang tidak cocok file_2, Anda dapat melakukan:

sed -n -e '/pattern/w file_1' -e '/pattern/!w file_2' input.txt

atau

sed -n '/pattern/!{p;d}; w file_1' input.txt > file_2

Penjelasan

  1. /pattern/!{p;d};
    • /pattern/!- negasi - jika suatu baris tidak mengandung pattern.
    • p - cetak ruang pola saat ini.
    • d- hapus spasi pola. Mulai siklus selanjutnya.
    • jadi, jika suatu baris tidak mengandung pola, ia akan mencetak baris ini ke output standar dan memilih baris berikutnya. Output standar diarahkan ke file_2dalam kasus kami. Bagian selanjutnya darised skrip ( w file_1) tidak tercapai sementara garis tidak cocok dengan pola.
  2. w file_1- jika sebuah baris berisi pola, /pattern/!{p;d};bagian tersebut dilewati (karena dieksekusi hanya ketika pola tidak cocok) dan, dengan demikian, baris ini menuju ke file_1.

Bisakah Anda menambahkan lebih banyak penjelasan ke solusi terakhir?
yukashima huksay

@aran Penjelasan ditambahkan. Juga perintah diperbaiki - file_1dan file_2ditukar ke urutan yang benar.
MiniMax

0

Saya menyukai sedsolusinya karena tidak bergantung pada bashisme dan memperlakukan file output pada pijakan yang sama. AFAIK, tidak ada alat Unix mandiri yang melakukan apa yang Anda inginkan sehingga Anda harus memprogramnya sendiri. Jika kita meninggalkan pendekatan pisau tentara Swiss, kita bisa menggunakan bahasa scripting (Perl, Python, NodeJS).

Inilah yang akan dilakukan di 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)));

Contoh penggunaan

# 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

Jika Anda tidak keberatan dengan penggunaan Python dan sintaks ekspresi reguler yang berbeda:

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

Pemakaian

./match-split.py PATTERN FILE-MATCH FILE-NOMATCH

Contoh

printf '%s\n' foo bar baz | python3 match-split.py '^b' b.txt not-b.txt
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.