grep Rückgabewert
Primel
- perl
Hi,
ich hab folgendes Problem...
Auf meinem Unix:
#grep "133.8.165.2 " /opt/named/hosts/masters/*; echo $?;
1
Mit Perl:
print system("grep "133.8.165.2 " /opt/named/hosts/masters/*");
256
Was mach ich falsch? Ich hab auch schon den * maskiert, aber macht kein Unterschied. Danke für Rat.
ciao,
Primel
Auf meinem Unix:
#grep "133.8.165.2 " /opt/named/hosts/masters/*; echo $?;
1
Ich habe keine Ahnung, welchen Rückgabewert du erwartest.
Mit Perl:
print system("grep "133.8.165.2 " /opt/named/hosts/masters/*");
256
Das ist der Status des System-Aufrufs, nicht der Rückgabewert der grep-Operation. Von daher erscheint mir die Divergenz beider Werte logisch.
Was mach ich falsch?
Du liest die Doku zu system nicht:
"The return value is the exit status of the program as returned by the wait call. To get the actual exit value, shift right by eight (see below). ... This is not what you want to use to capture the output from a command, for that you should use merely backticks or qx//, as described in "STRING
" in perlop."
Alles klar? :)
Siechfred
Auf meinem Unix:
#grep "133.8.165.2 " /opt/named/hosts/masters/*; echo $?;
1Ich habe keine Ahnung, welchen Rückgabewert du erwartest.
Mit Perl:
print system("grep "133.8.165.2 " /opt/named/hosts/masters/*");
256Das ist der Status des System-Aufrufs, nicht der Rückgabewert der grep-Operation. Von daher erscheint mir die Divergenz beider Werte logisch.
Was mach ich falsch?
Du liest die Doku zu system nicht:
"The return value is the exit status of the program as returned by the wait call. To get the actual exit value, shift right by eight (see below). ... This is not what you want to use to capture the output from a command, for that you should use merely backticks or qx//, as described in "
STRING
" in perlop."Alles klar? :)
Siechfred
Hi,
wenn man weiterliest:
wait call
Behaves like the wait(2) system call on your system: it waits for a child process to terminate and returns the pid of the deceased process, or -1 if there are no child processes. The status is returned in $? . Note that a return value of -1 could mean that child processes are being automatically reaped, as described in perlipc.
$? ist der returncode vom wait system call, deshalb verstehe ich es ja nicht ...
Naja was ich jetzt geblickt habe, dass ich es mit (?>>8) rausbekomme.
Und anstatt:
if (system("grep "133.8.165.2 " $ZONE/*") != 1 )|| system("grep "133.8.177.2 " $ZONE/*") != 1)
{
print "yeah";
}
Naja jetzt muss ich halt ein paar Zeilen mehr schreiben ;(
system("grep "133.8.165.2 " $ZONE/\");
$exit_code1 = ($?>>8);
system("grep "133.8.177.2 " $ZONE/\");
$exit_code2 = ($?>>8);
if ($exit_code1 != 1 || $exit_code2 != 1)
{
print "yeah";
}
thx,
Primel
Naja jetzt muss ich halt ein paar Zeilen mehr schreiben ;(
system("grep "133.8.165.2 " $ZONE/\");
$exit_code1 = ($?>>8);
system("grep "133.8.177.2 " $ZONE/\");
$exit_code2 = ($?>>8);
if ($exit_code1 != 1 || $exit_code2 != 1)
{
print "yeah";
}
Wozu so umständlich? Perl hat eine exzellente grep-Funktion, die mindestens so leistungsfähig ist wie das grep-Programm deines Unix. Und um mehrere Dateien nach einem Shell-Pattern zu suchen, gibt es die glob-Funktion. Und die beiden Pattern kann man auch noch zu einem kombinieren.
Bist Du Dir übrigens bewußt, das bei den meisten grep-Implementationen der Punkt eine besondere Bedeutung hat?
Alexander
Naja jetzt muss ich halt ein paar Zeilen mehr schreiben ;(
system("grep "133.8.165.2 " $ZONE/\");
$exit_code1 = ($?>>8);
system("grep "133.8.177.2 " $ZONE/\");
$exit_code2 = ($?>>8);
if ($exit_code1 != 1 || $exit_code2 != 1)
{
print "yeah";
}Wozu so umständlich? Perl hat eine exzellente grep-Funktion, die mindestens so leistungsfähig ist wie das grep-Programm deines Unix. Und um mehrere Dateien nach einem Shell-Pattern zu suchen, gibt es die glob-Funktion. Und die beiden Pattern kann man auch noch zu einem kombinieren.
Bist Du Dir übrigens bewußt, das bei den meisten grep-Implementationen der Punkt eine besondere Bedeutung hat?
Alexander
Hi
wäre das dann in etwa die Alternative?
foreach my $filename (glob("$ZONE/*")) {
open (READ,$filename) or die "$!";
@bar=<READ>;
print grep(/133.8.165.2/, @bar);
close (READ) or die "$!";
}
Oder hast du mal ein Beispiel für ein unix-emulierter "grep", wie man das am besten mit glob&grep in Perl macht?
ciao
Primel
Bist Du Dir übrigens bewußt, das bei den meisten grep-Implementationen der Punkt eine besondere Bedeutung hat?
Alexander
Hi
wäre das dann in etwa die Alternative?
foreach my $filename (glob("$ZONE/*")) {
open (READ,$filename) or die "$!";
@bar=<READ>;
print grep(/133.8.165.2/, @bar);wobei ich ja true oder false benötige und nicht den output;
close (READ) or die "$!";
}
So in der Art. Den Dateinamen solltest Du noch in die die()s mit reinnehmen, "Permission denied" bei 200 Dateien ist nicht wirklich hilfreich. ;-)
ABER: /133.8.165.2/ matcht auch 713398716529, sogar 133k8165W201, was in Deinem (vermutlichen) Anwendungsfall aber schlimmer ist: Es matcht 133.8.165.20 bis 133.8.165.29 und 133.8.165.200 bis 133.8.165.255. Das macht das eigenständige grep-Programm übrigens auch. Bitte informiere Dich anhand der Perl-Dokumentation über die Besonderheiten des Punkts in Regular Expressions. Außerdem suchst Du, je nach genauem Dateiformat, einen Zeilenanfang, ein Zeilenende, Leerzeichen oder ähnliche Trennzeichen.
Wenn Du von Perls grep nur wissen willst, ob es einen oder mehrere Treffer gibt, nutze es in einem boolschen Kontext, sprich:
if (grep /irgendwas/,@liste) {
print "gefunden!\n";
} else {
print "nix da!\n";
}
Nachteil von grep, egal ob extern oder in Perl eingebaut: Es geht immer über die GESAMTE Liste/Datei/Array, auch wenn Dir die Aussage "mindestens ein Treffer gefunden" reicht.
Besonders schlimm in Deinem Beispiel: Du ziehst die Dateien jeweils vollständig in den Speicher, nur um sie einmal durch grep durchlaufen zu lassen. if (grep /pattern/,<FILE>) könnte Perl vielleicht noch optimieren, so das immer nur eine Zeile in den Speicher gezogen wird, aber Dein Beispiel verhindert eine solche Optimierung. Zum Killer wird das besonders dann, wenn Deine Dateien viele Zeilen enthält, den jeder String in Perl enthält unsichtbar, aber im Speicherverbrauch erkennbar, noch etliche Bytes an Verwaltungsinformationen. Bei einer Log-Datei à la Apache kommen da leicht mal ein paar Megabyte nur an Verwaltungsdaten zusammen.
Ich würde Dir raten, die Datei "von Hand" zeilenweise zu lesen und beim ersten Treffer sofort abzubrechen.
use IO::File;
sub hasPattern
{
my $fn=shift;
my $f=IO::File->new($fn,'<') or die "$fn: $!";
local $_; # nicht $_ des Aufrufers verändern!
while (<$f>) {
return 1 if /pattern/; # (1)
}
$f->close(); # (2)
return; # (3)
}
print "$filename\n" if hasPattern($filename);
IO::File hat hier gegenüber dem normalen open einen Riesenvorteil: Sobald das Programm aus dem Scope, in dem die Variable mit dem IO::File-Objekt definiert ist, läuft, wird die durch IO::File repräsentierte Datei automatisch geschlossen. Das passiert bei (1), und da nach (2) die Funktion auch gleich beendet ist, ist (2) prinzipiell überflüssig. Es ist aber guter Stil, die Datei so bald wie möglich zu schließen, denn Datei-Handles gibt es nicht in unbegrenzter Menge.
(3): return ohne alles am Ende liefert false in *jedem* Kontext. return 0, return '' oder return undef würde im List-Kontext *true* ergeben.
Oder hast du mal ein Beispiel
Nur Fragmente, wenn Du eine vollständige Lösung von mir haben möchtest, fällt das unter kostenpflichtige Arbeit.
Alexander