कैसे grep के साथ दो फ़ाइलों के लिए एक आउटपुट को विभाजित करने के लिए?


14

मेरे पास एक स्क्रिप्ट है mycommand.shजिसे मैं दो बार नहीं चला सकता। मैं आउटपुट को दो अलग-अलग फाइलों में विभाजित करना चाहता हूं, जिसमें एक फ़ाइल एक रेखा से मेल खाती है और एक रेखा से मेल खाती है जो एक रेखा से मेल नहीं खाती है। मेरी इच्छा है कि मूल रूप से ऐसा कुछ हो:

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

मुझे पता है कि मैं सिर्फ एक फाइल के लिए आउटपुट को रीडायरेक्ट कर सकता हूं और फिर दो अलग-अलग greps के साथ -v विकल्प के बिना और उनके आउटपुट को दो अलग-अलग फाइलों में रीडायरेक्ट कर सकता हूं। लेकिन मैं सोच रहा था कि अगर यह एक grep के साथ करना संभव था।

तो, क्या यह संभव है कि मैं एक पंक्ति में क्या चाहता हूं?

जवाबों:


20

इसे पूरा करने के कई तरीके हैं।

जाग का उपयोग करना

निम्नलिखित coolregexफाइल 1 से मेल खाती कोई भी लाइनें भेजता है । अन्य सभी लाइनें file2 पर जाती हैं:

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

यह काम किस प्रकार करता है:

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

    नियमित अभिव्यक्ति से मेल खाती कोई भी रेखाएँ coolregexमुद्रित होती हैं file1। फिर, हम सभी शेष कमांड को छोड़ देते हैं और nextलाइन पर शुरू करने के लिए कूदते हैं ।

  2. 1

    अन्य सभी लाइनों को stdout में भेजा जाता है। 1है प्रिंट के लिए awk's cryptic शॉर्टहैंड।

कई धाराओं में विभाजित करना भी संभव है:

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

प्रक्रिया प्रतिस्थापन का उपयोग करना

यह अजीब समाधान की तरह सुरुचिपूर्ण नहीं है, लेकिन पूर्णता के लिए, हम प्रक्रिया प्रतिस्थापन के साथ संयुक्त कई greps का उपयोग कर सकते हैं:

./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 के बजाय सिर्फ एक और awk किए बिना इसे कई फाइलों में विभाजित करना संभव है? मेरा मतलब इस तरह से है कि रेग्जेस उदाहरण के लिए ओवरलैप कर सकते हैं।
युकाशिमा हूकसे

1
@ यार हां, जाग बहुत लचीली है। शायद ही कोई ऐसा करता है कि यह इस बात पर निर्भर करेगा कि रीगेक्स कैसे ओवरलैप होता है।
जॉन 1024

मैं एक समाधान देखना पसंद करूंगा, भले ही वह ओवरलैपिंग रीगेक्स का समर्थन न करे। ओवरलैप करने से मेरा मतलब है कि सबसेट का चौराहा नीरवता से खाली नहीं है।
युकाशिमा हूकसे

1
@aran मैंने दोनों तरीकों के लिए कई धाराओं के साथ उत्तर उदाहरणों में जोड़ा है।
जॉन 1024

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

क्या आप अंतिम समाधान के लिए कुछ और स्पष्टीकरण जोड़ सकते हैं?
युकाशिमा हूकसे

@aran स्पष्टीकरण जोड़ा गया। इसके अलावा कमांड को सही किया गया है - file_1और file_2सही क्रम में स्वैप किया गया।
मिनीमेक्स

0

मुझे sedसमाधान पसंद आया क्योंकि यह बशीशियों पर भरोसा नहीं करता है और आउटपुट फाइलों को एक ही पायदान पर मानता है। AFAIK, कोई स्टैंडअलोन यूनिक्स टूल नहीं है जो आपको चाहिए तो आप इसे स्वयं प्रोग्राम करने की आवश्यकता होगी। यदि हम स्विस सेना के चाकू के दृष्टिकोण को छोड़ देंगे, तो हम किसी भी स्क्रिप्टिंग भाषा (पर्ल, पायथन, नोडजेएस) का उपयोग कर सकते हैं।

इस तरह से यह 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.