2010-09-12 12 views
8

To właśnie mój kod Perl wygląda na monitoring a Unix folder:Czy mamy autochomp w Perlu?

#!/usr/bin/perl 
use strict; 
use warnings; 
use File::Spec::Functions; 

my $date = `date`; chomp $date; 
my $datef = `date +%Y%m%d%H%M.%S`; chomp $datef; 
my $pwd  = `pwd`; chomp $pwd; 

my $cache = catfile($pwd, "cache"); 
my $monitor = catfile($pwd, "monme"); 
my $subject = '...'; 
my $msg  = "..."; 
my $sendto = '...'; 
my $owner = '...'; 

sub touchandmail { 
    `touch $cache -t "$datef"`; 
    `echo "$msg" | mail -s "$subject" $owner -c $sendto`; 
} 

while(1) { 

    $date = `date`; chomp $date; 
    $datef = `date +%Y%m%d%H%M.%S`; chomp $datef; 

    if (! -e "$cache") { 
     touchandmail(); 
    } elsif ("`find $monitor -newer $cache`" ne "") { 
     touchandmail(); 
    } 
    sleep 300; 
} 
  • Aby wykonać chomp po każdym zadanie nie wygląda dobrze. Czy jest jakiś sposób zrobienia "autochomp"?

  • Jestem nowy w Perlu i prawdopodobnie nie napisałem tego kodu w najlepszy sposób. Wszelkie sugestie dotyczące ulepszenia kodu są mile widziane.

+1

Warto również pamiętać, że 'chomp' może przyjmować listę:' chomp ($ foo, $ bar, $ fubb) '. – FMc

Odpowiedz

14

Don w takim razie użyj powłoki.

#! /usr/bin/perl 

use warnings; 
use strict; 

use Cwd; 
use POSIX qw/ strftime /; 

my $date = localtime; 
my $datef = strftime "%Y%m%d%H%M.%S", localtime; 
my $pwd  = getcwd; 

Wynik jest nieco inna: wyjście komendy date zawiera strefę czasową, ale wartość $date powyżej nie będzie. Jeśli to jest problem, postępuj zgodnie z doskonałą sugestią podaną poniżej przez Chas. Owens i użyj formatu strftime, aby uzyskać żądany format.

Twój sub

sub touchandmail { 
    `touch $cache -t "$datef"`; 
    `echo "$msg" | mail -s "$subject" $owner -c $sendto`; 
} 

zawiedzie cicho, jeśli coś pójdzie nie tak. Ciche niepowodzenia są nieprzyjemne. Lepiej byłoby kod wzdłuż linii

sub touchandmail { 
    system("touch", "-t", $datef, $cache) == 0 
    or die "$0: touch exited " . ($? >> 8); 

    open my $fh, "|-", "mail", "-s", $subject, $owner, "-c", $sendto 
    or die "$0: could not start mail: $!"; 

    print $fh $msg 
    or warn "$0: print: $!"; 

    unless (close $fh) { 
    if ($! == 0) { 
     die "$0: mail exited " . ($? >> 8); 
    } 
    else { 
     die "$0: close: $!"; 
    } 
    } 
} 

Korzystanie system zamiast backticks jest bardziej wyrazisty swojej intencji bo backticks są do przechwytywania danych wyjściowych. Formularz system(LIST) pomija powłokę i musi martwić się cytowaniem argumentów.

Uzyskanie efektu powłoki rurociągu echo ... | mail ... bez powłoki oznacza, że ​​musimy sami wykonać prace hydrauliczne, ale korzyści - podobnie jak w przypadku system(LIST) - nie muszą martwić się o cytowanie powłoki. Powyższy kod wykorzystuje wiele-argumentu open:

Przez trzy lub więcej argumentów Jeśli tryb jest '|-', nazwa pliku jest interpretowana jako polecenie, do którego wyjście ma być wyprowadzony, a jeżeli TRYB jest '-|', nazwa pliku jest interpretowana jako polecenie, które potoki wyprowadzają do nas.W dwuargumentowej (i jednoargumentowej) formie należy zastąpić myślnik ('-') poleceniem. Więcej przykładów na ten temat można znaleźć w artykule Using open for IPC in perlipc.

open powyżej widłami proces mail i $fh jest podłączony do standardowego wejścia. Proces macierzysty (kod nadal działający touchandmail) pełni rolę echo z print $fh $msg. Wywołanie close wypłukuje uchwytu za I/bufory O plus trochę więcej, ponieważ, jak otworzyliśmy go:

Jeśli filehandle pochodził z rurami open, close zwraca false, jeżeli jeden z pozostałych syscalli zaangażowanych awarii lub jeżeli jej programu wychodzi z niezerowym stanem. Jeśli jedyny problem polegał na tym, że program zakończył się niezerowo, $! zostanie ustawiony na 0. Zamknięcie rury również czeka na zakończenie procesu na rurze - w przypadku, gdy chcesz spojrzeć na wyjście rury - i niejawnie umieszcza wartość statusu wyjścia tej komendy na $? i ${^CHILD_ERROR_NATIVE}.

+0

dzięki za sugestie dotyku i wiadomości e-mail. Myślałem o włączeniu do tego obsługi błędów. – Lazer

+0

co to jest "" | - "'? – Lazer

+0

@Lazer: '| -' otwiera potok i przekierowuje wszystko zapisane do uchwytu pliku na standardowe wejście nowego procesu. –

5

Spróbuj umieścić go w funkcji:

sub autochomp { 
    my $command = shift; 
    my $retval = `$command`; 
    chomp $retval; 
    return $retval; 
} 

a następnie zadzwonić, że za każde polecenie, które chcesz wykonać, a następnie chomp.

4

Użyj DateTime lub innego z modułów daty w CPAN zamiast narzędzia daty.

Na przykład:

use DateTime; 

my $dt = DateTime->now; 
print $dt->strftime('%Y%m%d%H%M.%S'); 
+1

Tylko dla daty "POSIX" jest potencjalnie jeszcze łatwiejszy/lżejszy: 'use POSIX qw/strftime /; my $ date = strftime "% Y% m% d% H% M.% S", czas lokalny; ' – Telemachus

2

Jest możliwość przypisania i chomp w jednej linii przy użyciu następującej składni:

chomp (my $date = `date`); 

Co do mówienia więcej Perlishly, jeśli okaże się, powtarzając te same rzecz w kółko, rzuć na sub:

sub assign_and_chomp { 

    my @result; 
    foreach my $cmd (@_) { 
     chomp (my $chomped = $cmd); 
     push @result, $chomped; 
    } 
    return @result; 
} 

my ($date , $datef , $pwd) 

    = assign_and_chomp (`date` , `date +%Y%m%d%H%M.%S` , `pwd`); 
+0

[' chomp'] (http://perldoc.perl.org/functions/chomp.html) "zwraca całkowita liczba znaków usuniętych ze wszystkich argumentów, "nie wyniki chomped. –

+0

@ gbacon: Dlatego jestem 'chomp'ing skalarami jeden po drugim – Zaid

+1

Ale twoja pętla przesuwa wartości zwracane z' chomp' na '@ result'. –

6

bardziej ogólnie moduł IO::All rzeczywiście dostarczyć równoważnik autochomp:

use IO::All; 
# for getting command output: 
my @date = io("date|")->chomp->slurp; 
#$date[0] contains the chomped first line of the output 

lub bardziej ogólnie:

my $fh = io("file")->chomp->tie; 
while (<$fh>) { 
# no need to chomp here ! $_ is pre-chomped 
} 

przyznane, w tym szczególnym przypadku date Zgodziłbym z innymi odbierającymi, że prawdopodobnie lepiej korzystasz z jednego z modułów DateTime, ale jeśli po prostu czytasz w pliku i chcesz, aby wszystkie twoje linie były chomp ed, a następnie IO::All z zastosowanymi opcjami chomp i tie jest bardzo wygodny.

Należy również pamiętać, że sztuczka chomp nie działa, gdy sapie całą treść uchwytu bezpośrednio na skalar (tak po prostu jest zaimplementowany).