2016-07-06 7 views
1

Ich versuche, die Ausgabe des Befehls als ein anderer Benutzer ausgeführt zu erfassen mit:Wie STDERR in Perl von einem Befehl in Rohr ausgeführt bekommen mit su -c

my $command = qq(sudo su - <username> -c '/usr/bin/whatever'); 
my $pid = open $cmdOutput, "-|", $command; 

Wie kann ich die STDERR erfassen von /usr/bin/whatever?

Ich versuchte

$pid = open $cmdOutput, "-|", $command || die " something went wrong: $!"; 

aber es sieht so aus, die möglichen Fehler der „offenen“ selbst zu erfassen.

Ich habe auch versucht

my $command = qq(sudo su - <username> -c '/usr/bin/whatever' 2>/tmp/error.message); 

die die STDERR in die Datei umleitet, die ich später analysieren können, aber ich wollte etwas einfachere Lösung.

Auch ich möchte nur Kernmodule verwenden.

+0

Warum sagst du * "Ich möchte auch keine externen Module verwenden" * und was meinst du mit "extern"? – Borodin

+0

@Borodin Nun, ich darf keine CPAN-Module oder Module installieren, die nicht Teil der Perl-Distribution sind. Ich habe meine Frage korrigiert :) – taiko

+1

Wenn Sie CPAN-Module nicht verwenden möchten, verwenden Sie die falsche Sprache. Wenn Sie "nicht dürfen" CPAN-Module verwenden, dann haben Sie den falschen Job :-) –

Antwort

3

Dies ist gründlich abgedeckt in perlfaq8. Da Sie eine Piped Open verwenden, sind die relevanten Beispiele diejenigen, die open3 aus dem Kernmodul IPC::Open3 gehen.

Eine andere Option ist die Verwendung IPC::Run für die Verwaltung Ihrer Prozesse, und die pump Funktion wird tun, was Sie brauchen. Die IPC::Open3 Dokumentation sagt für IPC::Run

Dies ist ein CPAN-Modul, das eine bessere Fehlerbehandlung und mehr Einrichtungen als Open3 hat.

mit einem dieser können Sie STDOUT und STDERR getrennt oder zusammen manipulieren, je nach Bedarf. Für eine bequeme und vollständige Ausgabeerfassung siehe auch Capture::Tiny.

Im Gegensatz zur 2>output Umleitung gibt es keine elementaren Methoden für die offene Pipe.


Wenn Sie nichts dagegen haben die Ströme Vermischung oder STDOUT ganz zu verlieren, eine weitere Option ist

my $command = 'cmd 2>&1 1>/dev/null'   # Remove 1>/dev/null to have both 
my $pid = open my $cmdOutput, "-|", $command; 

while (<$cmdOutput>) { print }    # STDERR only 

Die erste Umleitung STDERR Stream mit STDOUT verschmilzt, damit Sie sie beide bekommen, und gemischt (mit STDOUT gepuffert, so kann es gut laufen. Die zweite Umleitung sendet die STDOUT weg, so dass Sie nur die STDERR des Befehls aus dem Handle lesen.


Die Frage ist, über einen externen Befehl ausführen open verwenden, aber ich möchte zu erwähnen, dass die kanonischen und einfache qx (Backticks) können auf die gleiche Art und Weise verwendet werden. Es gibt die STDOUT zurück, so dass die Umleitung genau wie oben benötigt wird, um STDERR zu erhalten.Der Vollständigkeit halber:

my $cmd = 'cmd_to_execute'; 
my $allout = qx($cmd 2>&1);    # Both STDOUT and STDERR in $out, or 
my $stderr = qx($cmd 2>&1 1>/dev/null); # Only STDERR 
my $exit_status = $?; 

Die qx legt den Exit-Code Kindprozess (Status) in $?. Dies kann dann auf Fehlermodi untersucht werden; siehe eine Zusammenfassung in der qx Seite oder eine sehr gründliche Diskussion in I/O operators in perlop.

Beachten Sie, dass die STDERR, die auf diese Weise zurückgegeben wird, aus dem Befehl ist, wenn es ausgeführt wurde. Wenn der Befehl selbst nicht ausgeführt werden konnte (für einen Tippfehler im Befehlsnamen oder fork aus irgendeinem Grund fehlgeschlagen), wird $?-1 und der Fehler wird in $! sein.

+0

@taiko Danke für die Zuschreibung. Ich weiß nicht, ob Sie noch Interesse daran haben, aber nur um Sie wissen zu lassen, dass ich dafür eine Zusammenfassung von 'qx' hinzugefügt habe. – zdim

+0

sah es. Bis jetzt habe ich IPC :: Open3 verwendet, wie du es vorgeschlagen hast und es macht seine Arbeit für mich. Aber ich mag qx auch. Niemals zuvor qx benutzt und ich werde es versuchen. Vielen Dank für die Zusammenfassung. – taiko

+1

@taiko Großartig - open3 ist ein mächtiges Werkzeug. Während 'qx' (Backticks) ist einfach und doch ziemlich ausreichend :) Danke für die Antwort! – zdim

0

Das gewünschte Ziel, zum Schreiben geöffnet, könnte dup sein() 'ed auf FD # 2

1

Wie zdim vorgeschlagen habe ich die IPC :: Open3 Modul für die Sache und ich habe so etwas wie dies bekam den Job für mich

$instanceCommand = qq(sudo su - <username> -c '<command>'); 
my ($infh,$outfh,$errfh,$pid); 
$errfh = gensym(); 
$pid = open3($infh, $outfh, $errfh, $instanceCommand); 
my $sel = new IO::Select; 
$sel->add($outfh,$errfh); 
while (my @ready = $sel->can_read){ 
     foreach my $fh (@ready){ 
      my $line =<$fh>; 
      if (not defined $line){ 
       $sel->remove($fh); 
       next; 
       } 
      if ($fh == $outfh){ 
       chomp($line); 
       #<----- command output processing -----> 
       } 
      elsif ($fh == $errfh){                      
       chomp $line; 
       #<----- command error processing -----> 
       } 
      else { 
       die "Reading from something else\n"; 
       } 
      } 
     } 
waitpid($pid, 0); 

Vielleicht nicht vollständig kugelsicher, aber es funktioniert gut für mich. Auch während der Ausführung eines lustigen kaskadierten Skripts als < Befehl>.

+1

Habe das zufällig gesehen, wenn ich eine verwandte Frage beantworte. Schön, deine volle Lösung zu posten :) – zdim

+0

@zdim yeah. Es könnte jemandem helfen :) – taiko

+1

Richtig, kann es gut. Es ist auch interessant und nicht trivial. Ein Kommentar: Zum Vergleichen von Strings gibt es 'eq' Operator,' == 'ist für Zahlen. Ein Dateihandle ist eine "umhüllte" Nummer (Dateideskriptor), also würde ich 'eq' für sie verwenden. (Außerdem sagt 'Scalar :: Util :: looks_like_number', dass ein Dateihandle nicht wie eine Zahl aussieht.) Ich weiß, dass' IO :: Select' Dokumente ''=' in ihrem Beispiel verwenden und 'IPC :: Open3' erwähnt Ganzzahlen, aber diese sind lexikalische Dateihandles. (Ich kenne keinen Grund, '==' für Dateihandles zu verwenden.) – zdim