2010-02-06 13 views
17

Używam od lat linii poleceń Perl z opcją -ne, głównie do przetwarzania plików tekstowych w sposób, w jaki nie może tego zrobić sed. Przykład:Co każdy haker Perl powinien wiedzieć o Perl -ne?

cat in.txt | perl -ne "s/abc/def/; s/fgh/hij/; print;" > out.txt 

nie mam pojęcia, gdzie dowiedziałem się tego, a nie tylko czytać dzisiaj perlrun i stwierdził, że istnieją inne formy (perl -pe na przykład).

Co jeszcze należy wiedzieć o perl -ne?

+7

'perl -pe' jest odpowiedni dla przykładowego kodu. Użyj '-pe' i upuść' print' oświadczenie – mob

+0

Gee, dowiedziałem się o tym czytając wynik polecenia 'perl --help'. Zwykle używam tej opcji we wszystkich programach, z których korzystam, aby wiedzieć, co jest dostępne. –

+9

Cóż, naprawdę wszystko w Perlrun. Każdy haker Perla powinien przeczytać dokumentację. :) –

Odpowiedz

22

perl -ne 'CODE' jest odpowiednikiem programu

while (<>) { 
    CODE 
} 

perl -ane 'CODE' i perl -F/PATTERN/ -ane są również dobre idiomy wiedzieć. Są równoważne

while (<>) { 
    @F = split /\s+/, $_; 
    CODE 
} 

i

while (<>) { 
    @F = split /PATTERN/, $_; 
    CODE 
} 

przykład: rozszerzone grep:

perl -ne 'print if/REGEX1/&&!/REGEX2/&&(/REGEX3/||/REGEX4/&&!/REGEX5/)' input 

perl -F/,/ -ane 'print if $F[2]==4&&$F[3]ge"2009-07-01"&&$F[3]lt"2009-08-01"' file.csv 


Szczególnie mądry przykład, w którym wykorzystuje niedopasowane klamerek here.

+0

Z ciekawości, czy wiesz, co ich opętało, aby wybrać '@ F'? –

+2

Zgaduję, że "F" dotyczy pól. Myślę, że * może to być również konwencja awk. – Roboprog

+0

perldoc perlvar mówi "Tablica @F zawiera pola każdej linii" –

4

Opcja -i pozwala zrobić inline Zmiany:

perl -i -pe 's/abc/def/; s/fgh/hij/' file.txt 

lub zapisać kopię zapasową:

perl -i.bak -pe 's/abc/def/; s/fgh/hij/' file.txt 
6

Można podać więcej niż jeden -e klauzuli. Czasami mam linię poleceń, która zaczyna rosnąć, gdy udoskonalam operację wyszukiwania/wyciąg/mangulacji. jeśli coś pomylisz, otrzymasz "numer linii" informujący, który -e ma błąd.

Oczywiście, niektórzy mogą twierdzić, że jeśli masz więcej niż jedno lub dwa zdania, być może powinieneś umieścić to, co jest w scenariuszu, ale niektóre rzeczy naprawdę są po prostu wyrzucane, więc po co zawracać sobie głowę.

perl -n -e 'if (/good/)' -e '{ system "echo $_ >> good.txt"; }' \ 
-e 'elsif (/bad/)' -e '{ system "echo $_ >> bad.txt"; }' \ 
-e 'else' -e '{ system "echo $_ >> ugly.txt"; }' in.txt another.txt etc.txt 

Przypuszczalnie chcesz zrobić coś mniej banalnego niż grep/egrep na 3 plikach :-)

2

Lubię myśleć o perl -n jak wybieranie konkretnych bitów wejściowych i perl -p jak map dla wszystkich liniach wejście.

Jak już zauważył, że to możliwe, aby uzyskać efekt -p z -n i możemy emulować na odwrót:

$ echo -e "1\n2\n3" | perl -pe '$_="" if $_ % 2 == 0' 
1 
3

Pomijanie linii z next wydaje się bardziej naturalne, ale kod -p okłady w

LINE: 
while (<>) { 
    ...  # your program goes here 
} continue { 
    print or die "-p destination: $!\n"; 
} 

Zgodnie z projektem, next biegnie continue bloków:

Jeśli istnieje blok continue, jest on zawsze wykonywany tuż przed ponownym oceną warunku. Dzięki temu można go użyć do zwiększenia zmiennej pętli, nawet jeśli pętla została kontynuowana za pomocą instrukcji .

Przełącznik -l posiada dwa poręczne efekty:

  1. Z -n i -p automatycznie chomp każdy rekord wejściowy.
  2. Ustaw $\, więc każdy print niejawnie dodaje terminator.

Na przykład, aby pobrać pierwsze 10 porty UDP wymienione w /etc/services to polubisz

perl -ane 'print $F[1] if $F[1] =~ /udp/' /etc/services | head

ale oops:

7/udp9/udp11/udp13/udp17/udp19/udp37/udp39/udp42/ud...

Lepiej:

$ perl -lane 'print $F[1] if $F[1] =~ /udp/' /etc/services | head 
7/udp 
9/udp 
11/udp 
13/udp 
17/udp 
19/udp 
37/udp 
39/udp 
42/udp 
53/udp

Pamiętaj, że -n i -p może być w linii shebang też tak, aby zapisać powyższy oneliner jako skrypt:

#! /usr/bin/perl -lan 

BEGIN { 
    @ARGV = ("/etc/services") unless @ARGV; 
    open STDOUT, "|-", "head" or die "$0: head failed"; 
} 

print $F[1] if $F[1] =~ /udp/ 
12

Jest jeszcze jedna ważna rzecz, aby wiedzieć o perl -ne i perl -pe skrypty: są niejawnie używać <>.

"Dlaczego to takie ważne?" możesz zapytać.

Magiczny operator <> używa otwartej postaci 2 arg. Jeśli pamiętasz, 2 arg open zawiera specyfikację trybu z nazwą pliku w jednym argumencie. Wywołanie starego stylu do open FILE, $foo jest podatne na manipulowanie trybem plików. Szczególnie interesującym trybem w tym kontekście jest | - otwierasz uchwyt do potoku do procesu, który wykonujesz.

Być może myślisz "Wielka sprawa!", Ale tak jest.

  • Wyobraź sobie zadanie cron wykonane przez root'a do plików dziennika munge w jakimś katalogu.
  • Skrypt jest wywoływany jako script *.
  • Wyobraź sobie plik w tym katalogu o nazwie |rm -rf /.

Co się dzieje?

  1. Powłoka rozszerza * i otrzymujemy script file_1 file_2 '|rm -rf /' file_4
  2. Skrypt przetwarza file_1 i file_2.
  3. Następnie otwiera uchwyt do STDIN z rm -rf /.
  4. Następuje duża aktywność dysku.
  5. file_4 już nie istnieje, więc nie możemy go otworzyć.

Oczywiście możliwości są nieograniczone.

Możesz przeczytać more discussion of this issue at Perlmonks.

Morał z opowieści: uważaj z operatorem <>.

FWIW, Właśnie potwierdziłem, że to nadal jest problem z perl 5.10.0.

+0

To poważne rzeczy. Czy ktoś powiedział, że wadą korzystania z 3 argumentów otwartych, z wyraźnym trybem "odczytu", byłoby zastosowanie do implementacji operatora wokka <>? – Roboprog

+0

OK, czytałem wątek perlmonków. Mniejszość głosowa twierdzi, że szaleństwo jest cechą. "Spodziewacie się, że to po prostu otwiera listę plików do odczytania, ale robi więcej, i nie obchodzi nas, że chcemy czegoś bezpiecznego, prostego i prostego". Musi istnieć jakiś "pragmatyczny diament" pragma/moduł. Przegłosowałem tę odpowiedź ponad moją i mam nadzieję, że ostatecznie zostanie ona upowszechniona na szczycie listy. – Roboprog

+1

@Roboprog jest moduł CPAN: ['ARVG :: readonly'] (http://search.cpan.org/~davidnico/ARGV-readonly-0.01/lib/ARGV/readonly.pm). – rightfold

1

często używam sed lub awk ale bardzo lubię ten perl dopasowania funkcji wzór zabójcy:

$ cat my-input.txt 
git 111 HERE 2222 voila 333 
any 444 HERE none start 555 HERE 6 
svn 777 aaaa 8888 nothing 
two 222 HERE 9999 HERE 0000 

$ perl -nle 'print $a if (($a)=/HERE ([0-9]+)/)' my-input.txt 
2222 
6 
9999