उन फ़ाइलों को खोजें जिनमें फ़ाइल में कहीं भी कई कीवर्ड हैं


16

मैं एक निर्देशिका में सभी फाइलों को सूचीबद्ध करने का एक तरीका खोज रहा हूं, जिसमें मेरे द्वारा मांगे जाने वाले कीवर्ड का पूरा सेट है, कहीं भी फ़ाइल में।

इसलिए, खोजशब्दों को एक ही पंक्ति में प्रदर्शित होने की आवश्यकता नहीं है।

ऐसा करने का एक तरीका होगा:

grep -l one $(grep -l two $(grep -l three *))

तीन कीवर्ड सिर्फ एक उदाहरण है, यह सिर्फ दो या चार हो सकता है, और इसी तरह।

एक दूसरा तरीका जो मैं सोच सकता हूं वह है:

grep -l one * | xargs grep -l two | xargs grep -l three

एक तीसरा तरीका, जो एक और सवाल में दिखाई दिया , वह होगा:

find . -type f \
  -exec grep -q one {} \; -a \
  -exec grep -q two {} \; -a \
  -exec grep -q three {} \; -a -print

लेकिन निश्चित रूप से मैं यहां नहीं जा रहा हूं। मैं कुछ है कि कम लिखना पड़ता है, और संभवतः सिर्फ एक करने के लिए कॉल चाहते grep, awk, perlया इसी तरह की।

उदाहरण के लिए, मुझे पसंद है कि कैसे awkआप उन लाइनों से मेल खाते हैं जिनमें सभी कीवर्ड हैं , जैसे:

awk '/one/ && /two/ && /three/' *

या, केवल फ़ाइल नाम प्रिंट करें:

awk '/one/ && /two/ && /three/ { print FILENAME ; nextfile }' *

लेकिन मैं ऐसी फाइलें ढूंढना चाहता हूं जहां कीवर्ड फाइल में कहीं भी हों, जरूरी नहीं कि वह उसी लाइन पर हो।


पसंदीदा समाधान gzip के अनुकूल होगा, उदाहरण grepके लिए zgrepवैरिएंट है जो संपीड़ित फ़ाइलों पर काम करता है। मैं इसका उल्लेख क्यों करता हूं, यह है कि कुछ समाधान इस बाधा को देखते हुए अच्छी तरह से काम नहीं कर सकते हैं। उदाहरण के लिए, awkमुद्रण मिलान फ़ाइलों के उदाहरण में, आप ऐसा नहीं कर सकते:

zcat * | awk '/pattern/ {print FILENAME; nextfile}'

आपको कमांड को महत्वपूर्ण रूप से बदलने की जरूरत है, जैसे:

for f in *; do zcat $f | awk -v F=$f '/pattern/ { print F; nextfile }'; done

इसलिए, बाधा के कारण, आपको awkकई बार कॉल करने की आवश्यकता होती है , भले ही आप इसे केवल एक बार असम्पीडित फ़ाइलों के साथ कर सकें। और निश्चित रूप से, यह सिर्फ करना zawk '/pattern/ {print FILENAME; nextfile}' *और समान प्रभाव प्राप्त करना अच्छा होगा , इसलिए मैं ऐसे समाधानों को प्राथमिकता दूंगा जो इसे अनुमति देते हैं।


1
आपको उनके gzipअनुकूल होने की आवश्यकता नहीं है , बस zcatपहले फाइलें।
terdon

@terdon मैंने पोस्ट को संपादित किया है, यह बताते हुए कि मैं उल्लेख करता हूं कि फाइलें संपीड़ित हैं।
arekolek

एक बार या कई बार awk लॉन्च करने में बहुत अंतर नहीं है। मेरा मतलब है, ठीक है, कुछ छोटे ओवरहेड लेकिन मुझे संदेह है कि आप अंतर को भी नोटिस करेंगे। बेशक, यह संभव है कि जो भी स्क्रिप्ट हो उसे जागृत करें या उसे प्रभावित करें लेकिन यह एक पूर्ण विकसित कार्यक्रम बन जाता है, न कि एक-तरफा। क्या तुम यही चाहते हो?
terdon

@terdon व्यक्तिगत रूप से, मेरे लिए अधिक महत्वपूर्ण पहलू यह है कि कमांड कितना जटिल होगा (मुझे लगता है कि जब आप टिप्पणी कर रहे थे तो मेरा दूसरा संपादन आया था)। उदाहरण के लिए, grepसमाधान आसानी से grepएक के साथ कॉल उपसर्ग द्वारा आसानी से अनुकूलनीय हैं z, मेरे लिए फ़ाइल नामों को संभालने की भी कोई आवश्यकता नहीं है।
हैंकोल

हाँ, लेकिन वह है grep। AFAIK, केवल grepऔर catमानक "z-varants" है। मुझे नहीं लगता कि आपको for f in *; do zcat -f $f ...समाधान का उपयोग करने की तुलना में कुछ भी सरल मिलेगा । किसी भी चीज को पूरा प्रोग्राम बनाना होगा जो खोलने से पहले फ़ाइल स्वरूपों की जांच करता है या ऐसा करने के लिए लाइब्रेरी का उपयोग करता है।
terdon

जवाबों:


13
awk 'FNR == 1 { f1=f2=f3=0; };

     /one/   { f1++ };
     /two/   { f2++ };
     /three/ { f3++ };

     f1 && f2 && f3 {
       print FILENAME;
       nextfile;
     }' *

यदि आप स्वचालित रूप से gzipped फ़ाइलों को संभालना चाहते हैं, तो इसे एक लूप में चलाएं zcat(धीमी और अक्षम क्योंकि आप awkएक लूप में कई बार फोर्किंग करेंगे , एक बार प्रत्येक फ़ाइलनाम के लिए) या उसी एल्गोरिथ्म में फिर से लिखें perlऔर IO::Uncompress::AnyUncompressलाइब्रेरी मॉड्यूल का उपयोग करें जो कर सकते हैं कई अलग-अलग प्रकार की संपीड़ित फाइलें (gzip, zip, bzip2, lzop) को डिकम्प्रेस करें। या अजगर में, जिसमें संकुचित फ़ाइलों को संभालने के लिए मॉड्यूल भी हैं।


यहां एक perlसंस्करण है जो IO::Uncompress::AnyUncompressकिसी भी संख्या में पैटर्न और किसी भी फ़ाइल नाम (किसी भी सादे पाठ या संपीड़ित पाठ) की अनुमति देने के लिए उपयोग करता है।

पहले सभी आर्गों --को खोज पैटर्न के रूप में माना जाता है। सभी आर्ग के बाद --फाइलनाम के रूप में माना जाता है। इस काम के लिए आदिम लेकिन प्रभावी विकल्प से निपटने। बेहतर विकल्प से निपटने (जैसे -iकेस-असंवेदनशील खोजों के लिए एक विकल्प का समर्थन करने के लिए) Getopt::Stdया Getopt::Longमॉड्यूल के साथ हासिल किया जा सकता है ।

इसे ऐसे चलाएं:

$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt

(मैं फ़ाइलों {1..6}.txt.gzऔर {1..6}.txtयहाँ की सूची नहीं करूँगा ... वे परीक्षण के लिए "एक" "दो" "तीन" "तीन" "चार" "पाँच" और "छह" शब्दों में से कुछ या सभी होते हैं। उपरोक्त आउटपुट में सूचीबद्ध फाइलें। सभी तीन खोज पैटर्न शामिल हैं। अपने डेटा के साथ इसे स्वयं परखें)

#! /usr/bin/perl

use strict;
use warnings;
use IO::Uncompress::AnyUncompress qw(anyuncompress $AnyUncompressError) ;

my %patterns=();
my @filenames=();
my $fileargs=0;

# all args before '--' are search patterns, all args after '--' are
# filenames
foreach (@ARGV) {
  if ($_ eq '--') { $fileargs++ ; next };

  if ($fileargs) {
    push @filenames, $_;
  } else {
    $patterns{$_}=1;
  };
};

my $pattern=join('|',keys %patterns);
$pattern=qr($pattern);
my $p_string=join('',sort keys %patterns);

foreach my $f (@filenames) {
  #my $lc=0;
  my %s = ();
  my $z = new IO::Uncompress::AnyUncompress($f)
    or die "IO::Uncompress::AnyUncompress failed: $AnyUncompressError\n";

  while ($_ = $z->getline) {
    #last if ($lc++ > 100);
    my @matches=( m/($pattern)/og);
    next unless (@matches);

    map { $s{$_}=1 } @matches;
    my $m_string=join('',sort keys %s);

    if ($m_string eq $p_string) {
      print "$f\n" ;
      last;
    }
  }
}

एक हैश %patternsमें पैटर्न का पूरा सेट होता है जिसमें फ़ाइलों को कम से कम प्रत्येक सदस्य को शामिल करना होता $_pstringहै एक स्ट्रिंग है जिसमें उस .h की सॉर्ट की हुई कुंजी होती है। स्ट्रिंग $patternमें %patternsहैश से निर्मित एक पूर्व संकलित नियमित अभिव्यक्ति भी होती है ।

$patternप्रत्येक इनपुट फ़ाइल की प्रत्येक पंक्ति के खिलाफ तुलना की जाती है ( केवल एक बार /oसंकलित करने के लिए संशोधक का उपयोग करके $patternजैसा कि हम जानते हैं कि यह रन के दौरान कभी नहीं बदलेगा), और map()प्रत्येक फ़ाइल के लिए मैचों वाले हैश (% s) बनाने के लिए उपयोग किया जाता है।

जब भी वर्तमान फ़ाइल में सभी पैटर्न देखे गए हैं (यदि $m_string(सॉर्ट की गई कुंजियों के %sबराबर है तो $p_string) की तुलना करके , फ़ाइल नाम प्रिंट करें और अगली फ़ाइल पर जाएं।

यह विशेष रूप से तेज़ समाधान नहीं है, लेकिन अनुचित रूप से धीमा नहीं है। पहले संस्करण में 4 एमबी 58 सेकेंड की तीन फाइलों को खोजने के लिए 74 एमबी मूल्य की कंप्रेस्ड लॉग फाइल (कुल 937MB असम्पीडित) की तलाश की गई। यह वर्तमान संस्करण 1m13s लेता है। शायद आगे और भी आशाएँ हैं जिन्हें बनाया जा सकता है।

एक स्पष्ट अनुकूलन के साथ संयोजन के रूप में इस का उपयोग करने के लिए है xargs's -Pउर्फ --max-procsसमानांतर में फ़ाइलों के सबसेट पर एक से अधिक खोजें चलाने के लिए। ऐसा करने के लिए, आपको फ़ाइलों की संख्या की गणना करने की आवश्यकता है और आपके सिस्टम में कोर / cpus / थ्रेड्स की संख्या से विभाजित करें (और 1 जोड़कर गोल करें)। उदाहरण के लिए मेरे सैंपल सेट में 269 फाइलें खोजी गईं, और मेरे सिस्टम में 6 कोर (एएमडी 1090 टी) हैं, इसलिए:

patterns=(one two three)
searchpath='/var/log/apache2/'
cores=6
filecount=$(find "$searchpath" -type f -name 'access.*' | wc -l)
filespercore=$((filecount / cores + 1))

find "$searchpath" -type f -print0 | 
  xargs -0r -n "$filespercore" -P "$cores" ./arekolek.pl "${patterns[@]}" --

उस अनुकूलन के साथ, सभी 18 मिलान फ़ाइलों को खोजने में केवल 23 सेकंड लगे। बेशक, वही किसी भी अन्य समाधान के साथ किया जा सकता है। नोट: आउटपुट में सूचीबद्ध फ़ाइलनामों का क्रम अलग होगा, इसलिए यदि ऐसा हो तो बाद में क्रमबद्ध करने की आवश्यकता हो सकती है।

जैसा कि @arekolek ने उल्लेख किया है, कई zgrepएस इसके साथ find -execया xargsइसे काफी तेजी से कर सकते हैं, लेकिन इस स्क्रिप्ट को खोजने के लिए किसी भी संख्या के पैटर्न का समर्थन करने का लाभ है, और कई अलग-अलग प्रकार के संपीड़न से निपटने में सक्षम है।

यदि स्क्रिप्ट प्रत्येक फ़ाइल की केवल पहली 100 लाइनों की जांच करने के लिए सीमित है, तो यह 0.6 सेकंड में उन सभी (269 फाइलों के मेरे 74 एमबी नमूने में) के माध्यम से चलती है। यदि यह कुछ मामलों में उपयोगी है, तो इसे कमांड लाइन विकल्प (जैसे -l 100) में बनाया जा सकता है, लेकिन इसमें सभी मिलान फ़ाइलों को नहीं खोजने का जोखिम है ।


BTW, के लिए मैन पेज के अनुसार IO::Uncompress::AnyUncompress, संपीड़न प्रारूप समर्थित हैं:


एक अंतिम (मुझे आशा है) अनुकूलन। PerlIO::gzipमॉड्यूल का उपयोग करके (के रूप में डेबियन में पैक libperlio-gzip-perl) के बजाय IO::Uncompress::AnyUncompressमुझे अपने 74MB लॉग फ़ाइलों को संसाधित करने के लिए लगभग 3.1 सेकंड का समय मिला । इसके बजाय एक साधारण हैश का उपयोग करके कुछ छोटे सुधार भी किए गए थे Set::Scalar(जो IO::Uncompress::AnyUncompressसंस्करण के साथ कुछ सेकंड भी बचा था)।

PerlIO::gzipमें सबसे तेजी से पर्ल gunzip के रूप में सिफारिश की थी /programming//a/1539271/137158 (के लिए एक गूगल खोज के साथ पाया perl fast gzip decompress)

इसके xargs -Pसाथ प्रयोग करने से इसमें सुधार नहीं हुआ। वास्तव में यह 0.1 से 0.7 सेकंड तक कहीं भी इसे धीमा कर देता था। (मैंने चार रन की कोशिश की और मेरा सिस्टम पृष्ठभूमि में अन्य सामान करता है जो समय को बदल देगा)

मूल्य यह है कि स्क्रिप्ट का यह संस्करण केवल gzipped और असम्पीडित फ़ाइलों को संभाल सकता है। गति बनाम लचीलापन: इस संस्करण के लिए 3.1 सेकंड IO::Uncompress::AnyUncompressएक xargs -Pआवरण (या बिना 1m13s xargs -P) के साथ संस्करण के लिए 23 सेकंड बनाम ।

#! /usr/bin/perl

use strict;
use warnings;
use PerlIO::gzip;

my %patterns=();
my @filenames=();
my $fileargs=0;

# all args before '--' are search patterns, all args after '--' are
# filenames
foreach (@ARGV) {
  if ($_ eq '--') { $fileargs++ ; next };

  if ($fileargs) {
    push @filenames, $_;
  } else {
    $patterns{$_}=1;
  };
};

my $pattern=join('|',keys %patterns);
$pattern=qr($pattern);
my $p_string=join('',sort keys %patterns);

foreach my $f (@filenames) {
  open(F, "<:gzip(autopop)", $f) or die "couldn't open $f: $!\n";
  #my $lc=0;
  my %s = ();
  while (<F>) {
    #last if ($lc++ > 100);
    my @matches=(m/($pattern)/ogi);
    next unless (@matches);

    map { $s{$_}=1 } @matches;
    my $m_string=join('',sort keys %s);

    if ($m_string eq $p_string) {
      print "$f\n" ;
      close(F);
      last;
    }
  }
}

for f in *; do zcat $f | awk -v F=$f '/one/ {a++}; /two/ {b++}; /three/ {c++}; a&&b&&c { print F; nextfile }'; doneठीक काम करता है, लेकिन वास्तव में, मेरे grepसमाधान के रूप में 3 बार लेता है , और वास्तव में अधिक जटिल है।
इस्कॉलेक

1
OTOH, सादे पाठ फ़ाइलों के लिए यह तेज होगा। और एक ही एल्गोरिथ्म एक भाषा में कार्यान्वित किया गया है जिसमें संपीड़ित फ़ाइलों (जैसे पर्ल या अजगर) को पढ़ने के लिए समर्थन दिया गया है, जैसा कि मैंने सुझाव दिया था कि यह कई greps से अधिक तेज़ होगा। "जटिलता" आंशिक रूप से व्यक्तिपरक है - व्यक्तिगत रूप से, मुझे लगता है कि एकल awk या पर्ल या पायथन स्क्रिप्ट कई greps के साथ या बिना खोज के कम जटिल है ... @ टेर्डन का उत्तर अच्छा है, और क्या यह मॉड्यूल की आवश्यकता के बिना उल्लेख किया गया है (लेकिन प्रत्येक संपीड़ित फ़ाइल के लिए जटिंग को फोर्क करने की कीमत पर)
कैस

मुझे apt-get install libset-scalar-perlस्क्रिप्ट का इस्तेमाल करना था । लेकिन यह किसी भी उचित समय में समाप्त नहीं होता है।
हैंकोलेक जूल

कितनी और क्या आकार (संकुचित और असम्पीडित) वे फाइलें हैं जिन्हें आप खोज रहे हैं? दर्जनों या सैकड़ों छोटे-मध्यम आकार की फाइलें या हजारों बड़े?
कैस

यहां संपीड़ित फ़ाइलों (20 से 100 फ़ाइलों, 50 एमबी तक लेकिन ज्यादातर 5 एमबी से नीचे) के आकार का एक हिस्टोग्राम है । असम्पीडित एक ही दिखते हैं, लेकिन आकारों के साथ
10.34 से

11

रिकॉर्ड विभाजक सेट करें .ताकि awkपूरी फ़ाइल को एक पंक्ति के रूप में माना जाए :

awk -v RS='.' '/one/&&/two/&&/three/{print FILENAME}' *

इसी तरह से perl:

perl -ln00e '/one/&&/two/&&/three/ && print $ARGV' *

3
साफ। ध्यान दें कि यह पूरी फ़ाइल को मेमोरी में लोड करेगा और यह बड़ी फ़ाइलों के लिए एक समस्या हो सकती है।
terdon

मैंने शुरू में इसे बढ़ा दिया, क्योंकि यह आशाजनक लग रहा था। लेकिन मैं इसे gzipped फ़ाइलों के साथ काम करने के लिए नहीं कर सकता। for f in *; do zcat $f | awk -v RS='.' -v F=$f '/one/ && /two/ && /three/ { print F }'; doneआउटपुट कुछ नहीं।
१२:०६ पर हैंकोलेक

@arekolek वह पाश मेरे लिए काम करता है। क्या आपकी फाइलें ठीक से गज़ब की हैं?
जिमीज

zcat -f "$f"यदि कुछ फ़ाइलों को संपीड़ित नहीं किया गया है तो @arekolek की आपको आवश्यकता है ।
terdon

मैंने इसे असम्पीडित फ़ाइलों पर भी परीक्षण किया है और awk -v RS='.' '/bfs/&&/none/&&/rgg/{print FILENAME}' greptest/*.txtअभी भी कोई परिणाम नहीं grep -l rgg $(grep -l none $(grep -l bfs greptest/*.txt))देता है , जबकि अपेक्षित परिणाम देता है।
११:१३ बजे जूल

3

संपीड़ित फ़ाइलों के लिए, आप पहले प्रत्येक फ़ाइल और डीकंप्रेस पर लूप कर सकते हैं। फिर, अन्य उत्तरों के थोड़ा संशोधित संस्करण के साथ, आप कर सकते हैं:

for f in *; do 
    zcat -f "$f" | perl -ln00e '/one/&&/two/&&/three/ && exit(0); }{ exit(1)' && 
        printf '%s\n' "$f"
done

0अगर तीनों तार मिल गए तो पर्ल स्क्रिप्ट स्टेटस (सफलता) से बाहर निकल जाएगी । के }{लिए पर्ल शॉर्टहैंड है END{}। निम्नलिखित कुछ भी सभी इनपुट संसाधित होने के बाद निष्पादित किया जाएगा। यदि सभी तार नहीं मिले, तो स्क्रिप्ट एक गैर-0 निकास स्थिति के साथ बाहर निकल जाएगी। इसलिए, && printf '%s\n' "$f"फ़ाइल नाम केवल तभी प्रिंट करेगा जब तीनों पाए गए थे।

या, फ़ाइल को मेमोरी में लोड करने से बचने के लिए:

for f in *; do 
    zcat -f "$f" 2>/dev/null | 
        perl -lne '$k++ if /one/; $l++ if /two/; $m++ if /three/;  
                   exit(0) if $k && $l && $m; }{ exit(1)' && 
    printf '%s\n' "$f"
done

अंत में, यदि आप वास्तव में एक स्क्रिप्ट में पूरी बात करना चाहते हैं, तो आप कर सकते हैं:

#!/usr/bin/env perl

use strict;
use warnings;

## Get the target strings and file names. The first three
## arguments are assumed to be the strings, the rest are
## taken as target files.
my ($str1, $str2, $str3, @files) = @ARGV;

FILE:foreach my $file (@files) {
    my $fh;
    my ($k,$l,$m)=(0,0,0);
    ## only process regular files
    next unless -f $file ;
    ## Open the file in the right mode
    $file=~/.gz$/ ? open($fh,"-|", "zcat $file") : open($fh, $file);
    ## Read through each line
    while (<$fh>) {
        $k++ if /$str1/;
        $l++ if /$str2/;
        $m++ if /$str3/;
        ## If all 3 have been found
        if ($k && $l && $m){
            ## Print the file name
            print "$file\n";
            ## Move to the net file
            next FILE;
        }
    }
    close($fh);
}

ऊपर की स्क्रिप्ट foo.plको अपने में कहीं सहेज कर रखें $PATH, इसे निष्पादन योग्य बनाएं और इसे इस तरह से चलाएं:

foo.pl one two three *

2

अब तक प्रस्तावित सभी समाधानों में, grep का उपयोग करने वाला मेरा मूल समाधान सबसे तेज़ है, जो 25 सेकंड में खत्म होता है। इसकी कमी यह है कि यह कीवर्ड जोड़ने और हटाने के लिए थकाऊ है। इसलिए मैं एक स्क्रिप्ट के साथ आया (डब किया गया multi) जो व्यवहार को अनुकरण करता है, लेकिन वाक्यविन्यास को बदलने की अनुमति देता है:

#!/bin/bash

# Usage: multi [z]grep PATTERNS -- FILES

command=$1

# first two arguments constitute the first command
command_head="$1 -le '$2'"
shift 2

# arguments before double-dash are keywords to be piped with xargs
while (("$#")) && [ "$1" != -- ] ; do
  command_tail+="| xargs $command -le '$1' "
  shift
done
shift

# remaining arguments are files
eval "$command_head $@ $command_tail"

इसलिए अब, लेखन multi grep one two three -- *मेरे मूल प्रस्ताव के बराबर है और एक ही समय में चलता है। मैं zgrepइसके बजाय पहले तर्क के रूप में उपयोग करके आसानी से संपीड़ित फ़ाइलों पर इसका उपयोग कर सकता हूं ।

अन्य उपाय

मैंने दो रणनीतियों का उपयोग करके पायथन स्क्रिप्ट का भी प्रयोग किया: सभी कीवर्ड लाइन द्वारा लाइन में खोज, और कीवर्ड द्वारा संपूर्ण फ़ाइल कीवर्ड में खोज। मेरे मामले में दूसरी रणनीति तेज थी। लेकिन यह सिर्फ grep33 सेकंड में फिनिशिंग के साथ धीमी गति से काम कर रहा था। लाइन द्वारा लाइन कीवर्ड मिलान 60 सेकंड में समाप्त हो गया।

#!/usr/bin/python3

import gzip, sys

i = sys.argv.index('--')
patterns = sys.argv[1:i]
files = sys.argv[i+1:]

for f in files:
  with (gzip.open if f.endswith('.gz') else open)(f, 'rt') as s:
    txt = s.read()
    if all(p in txt for p in patterns):
      print(f)

स्क्रिप्ट terdon द्वारा दिए गए 54 सेकंड में समाप्त हो गया। वास्तव में इसमें 39 सेकंड की दीवार का समय लगा, क्योंकि मेरा प्रोसेसर डुअल कोर है। यह दिलचस्प है, क्योंकि मेरी पायथन लिपि ने दीवार के समय के 49 सेकंड (और grep29 सेकंड) लिया था।

कैस द्वारा स्क्रिप्ट उचित समय में समाप्त करने के लिए, यहां तक कि फ़ाइलों को साथ प्रोसेस किया गया था एक छोटी संख्या पर विफल रही grep, 4 सेकंड के तहत तो मैं यह को मारने के लिए किया था।

लेकिन उसका मूल awkप्रस्ताव, भले ही यह जितना धीमा हो grep, संभावित लाभ है। कुछ मामलों में, कम से कम मेरे अनुभव में, यह उम्मीद करना संभव है कि सभी कीवर्ड फ़ाइल के सिर में कहीं दिखाई दें, यदि वे फ़ाइल में हैं। यह इस समाधान को प्रदर्शन में एक नाटकीय बढ़ावा देता है:

for f in *; do
  zcat $f | awk -v F=$f \
    'NR>100 {exit} /one/ {a++} /two/ {b++} /three/ {c++} a&&b&&c {print F; exit}'
done

25 सेकंड के विपरीत, एक सेकंड के एक चौथाई में खत्म हो जाता है।

बेशक, हम उन खोजशब्दों को खोजने का लाभ नहीं उठा सकते हैं जो फाइलों की शुरुआत के पास होने के लिए जाने जाते हैं। ऐसे मामले में, बिना समाधान NR>100 {exit}63 सेकंड (दीवार के समय 50 सेकंड) लेता है।

असंपीड़ित फ़ाइलें

मेरे grepसमाधान और कैस के awkप्रस्ताव के बीच चल रहे समय में कोई महत्वपूर्ण अंतर नहीं है , दोनों निष्पादित करने के लिए एक दूसरे का एक अंश लेते हैं।

ध्यान दें कि इस तरह के चर FNR == 1 { f1=f2=f3=0; }को हर बाद की संसाधित फ़ाइल के लिए काउंटरों को रीसेट करने के लिए अनिवार्य है। इस प्रकार, इस समाधान के लिए कमांड को तीन स्थानों पर संपादित करने की आवश्यकता होती है यदि आप कोई कीवर्ड बदलना चाहते हैं या नए जोड़ना चाहते हैं। दूसरी ओर, grepआप केवल अपने | xargs grep -l fourइच्छित कीवर्ड को जोड़ या संपादित कर सकते हैं।

grepसमाधान का एक नुकसान जो कमांड प्रतिस्थापन का उपयोग करता है, वह है कि यह लटकाएगा यदि श्रृंखला में कहीं भी, अंतिम चरण से पहले, कोई मेल खाने वाली फाइलें नहीं हैं। यह xargsवैरिएंट को प्रभावित नहीं करता है क्योंकि पाइप को एक बार grepशून्य-शून्य स्थिति में वापस ले लिया जाएगा । मैंने अपनी स्क्रिप्ट का उपयोग करने के लिए अपडेट किया है xargsइसलिए मुझे इसे स्वयं हैंडल करने की आवश्यकता नहीं है, स्क्रिप्ट को सरल बना रहा है।


आपका अजगर समाधान पाश के साथ सी परत को नीचे धक्का से लाभ हो सकताnot all(p in text for p in patterns)
इरुवर

@iruvar सुझाव के लिए धन्यवाद। मैंने इसे (sans not) आज़माया है और यह 32 सेकंड में समाप्त हो गया है, इसलिए इसमें इतना सुधार नहीं है, लेकिन यह निश्चित रूप से अधिक पठनीय है।
बजे १:१६ बजे इस्कॉलेक

आप f1, f2, f3 के बजाय awk में एक साहचर्य सरणी का उपयोग कर सकते हैं, कुंजी = खोज-पैटर्न, वैल = काउंट के साथ
cas

@arekolek के PerlIO::gzipबजाय का उपयोग करके मेरा नवीनतम संस्करण देखें IO::Uncompress::AnyUncompress। अब मेरी 74MB लॉग फ़ाइलों को संसाधित करने के लिए 1m13s के बजाय केवल 3.1 सेकंड लगते हैं।
कैस

BTW, यदि आप पहले से चल रहे हैं eval $(lesspipe)(जैसे आपके .profile, आदि), तो आप lessइसके बजाय zcat -fऔर अपने forलूप रैपर का उपयोग कर सकते हैं, awkकिसी भी तरह की फ़ाइल को संसाधित करने में सक्षम हो जाएगा less(gzip, bzip2, xz, और अधिक)। अगर स्टडआउट एक पाइप है, तो यह पता लगाया जा सकता है कि स्टडआउट में धारा प्रवाहित होगी या नहीं।
कैस

0

एक अन्य विकल्प - फाइल के खिलाफ xargsचलने के लिए एक समय में एक शब्द फ़ीड करें grep। इसे वापस करने के लिए xargsजल्द से जल्द बाहर निकलने के लिए बनाया जा सकता है ( grepरिटर्न की 255जाँच करें xargs)। बेशक गोले के spawning और इस समाधान में शामिल होने की संभावना संभवतः इसे काफी धीमा कर देगा

printf '%s\n' one two three | xargs -n 1 sh -c 'grep -q $2 $1 || exit 255' _ file

और इसे लूप करने के लिए

for f in *; do
    if printf '%s\n' one two three | xargs -n 1 sh -c 'grep -q $2 $1 || exit 255' _ "$f"
    then
         printf '%s\n' "$f"
    fi
done

यह अच्छा लग रहा है, लेकिन मुझे यकीन नहीं है कि इसका उपयोग कैसे करें। क्या है _और file? क्या यह खोज कई फाइलों में तर्क और रिटर्न फाइल के रूप में पारित होगी जिसमें सभी कीवर्ड शामिल हैं?
टोकियोलेक जूल

@arekolek, ने एक लूप संस्करण जोड़ा। और के रूप में _, यह $0spawned खोल के रूप में पारित किया जा रहा है - यह उत्पादन में कमांड नाम के रूप में दिखाई देगा ps- मैं यहाँ मास्टर के लिए स्थगित कर देगा
iruvar
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.