البحث/قراءة ملف آخر من awk بناءً على محتويات الملف الحالي، هل هذا ممكن؟

StackOverflow https://stackoverflow.com/questions/144321

سؤال

أقوم بمعالجة ملف ضخم باستخدام (GNU) awk, ، (الأدوات الأخرى المتاحة هي:أدوات Linux Shell، بعض الإصدارات القديمة (>5.0) من Perl، ولكن لا يمكنها تثبيت الوحدات النمطية).

مشكلتي:إذا كانت بعض الحقول 1، وfield2، وfield3 تحتوي على X وY وZ، فلا بد لي من البحث عن ملف في دليل آخر يحتوي على field4 وfield5 في سطر واحد، وإدراج بعض البيانات من الملف الذي تم العثور عليه إلى الإخراج الحالي.

على سبيل المثال:

خط الملف الفعلي:

f1 f2 f3 f4 f5
X  Y  Z  A  B

الآن أنا بحاجة للبحث عن ملف آخر (في دليل آخر)، والذي يحتوي على سبيل المثال.

f1 f2 f3 f4
A  U  B  W

والكتابة إلى STDOUT $0 من الملف الأصلي، و f2 و f3 من الملف الذي تم العثور عليه، ثم قم بمعالجة السطر التالي من الملف الأصلي.

هل من الممكن أن تفعل ذلك مع awk?

هل كانت مفيدة؟

المحلول

اسمحوا لي أن أبدأ بالقول إن وصف مشكلتك ليس مفيدًا حقًا.في المرة القادمة، يرجى أن تكون أكثر تحديدًا:ربما تفوتك حلول أفضل بكثير.

لذا، من وصفك، أفهم أن لديك ملفين يحتويان على بيانات مفصولة بمسافات بيضاء.في الملف الأول، تريد مطابقة الأعمدة الثلاثة الأولى مع بعض أنماط البحث.إذا تم العثور عليه، فأنت تريد البحث عن كافة الأسطر في ملف آخر يحتوي على العمودين الرابع والخامس من السطر المطابق في الملف الأول.من تلك السطور، تحتاج إلى استخراج العمود الثاني والثالث ثم طباعة العمود الأول من الملف الأول والثاني والثالث من الملف الثاني.حسنًا، إليك ما يلي:

#!/usr/bin/env perl -nwa
use strict;
use File::Find 'find';
my @search = qw(X Y Z);

# if you know in advance that the otherfile isn't
# huge, you can cache it in memory as an optimization.

# with any more columns, you want a loop here:
if ($F[0] eq $search[0]
    and $F[1] eq $search[1]
    and $F[2] eq $search[2])
{
  my @files;
  find(sub {
      return if not -f $_;
      # verbatim search for the columns in the file name.
      # I'm still not sure what your file-search criteria are, though.
      push @files, $File::Find::name if /\Q$F[3]\E/ and /\Q$F[4]\E/;
      # alternatively search for the combination:
      #push @files, $File::Find::name if /\Q$F[3]\E.*\Q$F[4]\E/;
      # or search *all* files in the search path?
      #push @files, $File::Find::name;
    }, '/search/path'
  )
  foreach my $file (@files) {
    open my $fh, '<', $file or die "Can't open file '$file': $!";
    while (defined($_ = <$fh>)) {
      chomp;
      # order of fields doesn't matter per your requirement.
      my @cols = split ' ', $_;
      my %seen = map {($_=>1)} @cols;
      if ($seen{$F[3]} and $seen{$F[4]}) {
        print join(' ', $F[0], @cols[1,2]), "\n";
      }
    }
    close $fh;
  }
} # end if matching line

على عكس حل الملصق الآخر الذي يحتوي على الكثير من استدعاءات النظام، فإن هذا لا يعود إلى الصدفة على الإطلاق وبالتالي يجب أن يكون سريعًا جدًا.

نصائح أخرى

هذا هو نوع العمل الذي جعلني أنتقل من awk إلى Perl في المقام الأول.إذا كنت ستحقق ذلك، فقد تجد أنه من الأسهل بالفعل إنشاء برنامج نصي لـ Shell يقوم بإنشاء برنامج نصي (نصوص) awk للاستعلام ثم التحديث في خطوات منفصلة.

(لقد كتبت مثل هذا الوحش لقراءة/تحديث ملفات نمط windows-ini - إنه قبيح.أتمنى لو كان بإمكاني استخدام بيرل.)

غالبًا ما أرى التقييد "لا يمكنني استخدام أي وحدات Perl"، وعندما لا يكون هذا سؤالًا يتعلق بالواجب المنزلي، فغالبًا ما يكون ذلك بسبب نقص المعلومات فقط. نعم، حتى يمكنك استخدام CPAN يحتوي على إرشادات حول كيفية تثبيت وحدات CPAN محليًا دون الحصول على امتيازات الجذر.البديل الآخر هو أخذ الكود المصدري لوحدة CPAN ولصقه في برنامجك.

لا يساعد أي من هذا إذا كانت هناك قيود أخرى غير معلنة، مثل نقص مساحة القرص التي تمنع تثبيت (عدد كبير جدًا) من الملفات الإضافية.

يبدو أن هذا يعمل مع بعض ملفات الاختبار التي قمت بإعدادها لمطابقة الأمثلة الخاصة بك.من المحتمل أن يؤدي استخدام لغة Perl بهذه الطريقة (المتداخلة مع grep) إلى الإضرار بالأداء كثيرًا، على الرغم من ذلك...

## perl code to do some dirty work

for my $line (`grep 'X Y Z' myhugefile`) {
    chomp $line;
    my ($a, $b, $c, $d, $e) = split(/ /,$line);
    my $cmd = 'grep -P "' . $d . ' .+? ' . $e .'" otherfile';
    for my $from_otherfile (`$cmd`) {
        chomp $from_otherfile;
        my ($oa, $ob, $oc, $od) = split(/ /,$from_otherfile);
        print "$a $ob $oc\n";
    }
}

يحرر: استخدم حل tsee (أعلاه)، فهو مدروس جيدًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top