2008-10-01 6 views
5

Z powodu bardziej żmudnego dodawania hostów do monitorowania w Nagios (wymaga to zdefiniowania obiektu hosta, w przeciwieństwie do poprzedniego programu, który wymagał tylko IP i nazwa hosta), pomyślałem, że najlepiej będzie zautomatyzować to i byłby to świetny czas na naukę Perla, ponieważ wszystko, co wiem w tej chwili to C/C++ i Java.Perl: Chwytanie n-tego i mth ograniczonych słów z każdej linii w pliku

Plik czytam od wygląda następująco:

xxx.xxx.xxx.xxx hostname #comments. i.dont. care. about 

Wszystko czego chcę to pierwsze 2 pęczki znaków. Są to oczywiście obszary ograniczone, ale ze względu na ogólność, równie dobrze może być cokolwiek. Aby było bardziej ogólne, dlaczego nie pierwszy i trzeci, czwarty i dziesiąty? Z pewnością musi to być pewne działanie z użyciem wyrażenia regularnego, ale na wszelki wypadek zostawię ten tag na razie.

Odpowiedz

6

Jednoliterówka jest świetna, jeśli nie piszesz więcej Perla, aby obsłużyć wynik.

Bardziej ogólnie jednak, w ramach większego programu Perl, byś albo napisać niestandardowy wyrażenie regularne, na przykład:

if($line =~ m/(\S+)\s+(\S+)/) { 
    $ip = $1; 
    $hostname = $2; 
} 

... albo byłoby użyć podzielić operatora.

my @arr = split(/ /, $line); 
$ip = $arr[0]; 
$hostname = $arr[1]; 

Tak czy inaczej, dodaj logikę, aby sprawdzić nieprawidłowe dane wejściowe.

+0

Powiedziałbym, że bardziej idiomatyczne jest przypisywanie list: np. ($ Ip, $ nazwa hosta) = (1 $, 2 $) w pierwszym przypadku lub ($ ip, $ hostname) = (split '', $ line) [0,1] w drugim. (0,1 jest na wypadek, gdyby ludzie chcieli używać innych liczb, jeśli nie, ($ ip, $ hostname) = split '', linia $ zadziała poprawnie. –

+1

Nie, bardziej idiomatyczna byłaby "if (my ($ ip, $ hostname) = $ line = ~/(\ S +) \ s + (\ S +) /) {". – ysth

+1

Obaj macie rację, ale nie jestem zdania, że ​​idiomatyczne == lepiej . – slim

5

prosta-liner jest

perl -nae 'print "$F[0] $F[1]\n";' 

można zmienić separator z -F

7

Zwróćmy to pod kodem golfa! W oparciu o doskonałą odpowiedź Dawida, oto moje:

perl -ane 'print "@F[0,1]\n";' 

Edycja: Prawdziwa złożenie golf będzie wyglądać mniej więcej tak (goląc pięć uderzeń):

perl -ape '$_="@F[0,1] 
"' 

ale to jest mniej czytelny dla celów to pytanie jest. :-P

+0

Dobrze grał, sir. – Axeman

+0

Dzięki! Zmieniłem wpis czymś nawet golfistą, ale prawdopodobnie bardziej nieczytelnym. :-P –

3

David Nehme powiedział:

perl -nae 'print "$F[0] $F[1}\n"; 

który wykorzystuje przełącznik -a. Musiałem to sprawdzić:

-a turns on autosplit mode when used with a -n or -p. An implicit split 
    command to the @F array is done as the first thing inside the implicit 
    while loop produced by the -n or -p. 

czegoś się uczysz każdego dnia. -n powoduje każda linia mają być przekazane do

LINE: 
    while (<>) { 
     ...    # your program goes here 
    } 

I wreszcie -e jest sposób, aby bezpośrednio wprowadzić pojedynczą linię programu. Możesz mieć więcej niż -e. Większość tego była zerwaniem strony podręcznika perlrun(1).

+0

Tryb "autosplit" jest również znany jako "tryb awk", a użycie @F jako nazwy tablicy pochodzi z awk. – rjray

6

Oto ogólne rozwiązanie (jeśli nieco cofniemy się przed kodem golfowym).

#!/usr/bin/perl -n 
chop;      # strip newline (in case next line doesn't strip it) 
s/#.*//;     # strip comments 
next unless /\S/;   # don't process line if it has nothing (left) 
@fields = (split)[0,1]; # split line, and get wanted fields 
print join(' ', @fields), "\n"; 

Normalnie split dzieli przez białe znaki. Jeśli nie jest to, co chcesz (np parsowania /etc/passwd), można przekazać separator jako regex:

@fields = (split /:/)[0,2,4..6]; 

Oczywiście, jeśli jesteś parsowania plików okrężnicy rozdzielany, są również dobre szanse, że takie pliki don Mają komentarze i nie musisz ich rozbierać.

+0

Powinieneś prawie zawsze używać chomp zamiast siekać. chop zawsze usuwa ostatni znak z ciągu znaków. chomp usuwa bieżący terminator linii (zwykle "\ n") z ciągu znaków, jeśli jest obecny. Jeśli linia nie kończy się terminatorem, chomp nie robi nic. kotlet może usuwać rzeczy, których się nie spodziewasz. – cjm

+0

Sposób uniksowy polega na tym, że wszystkie pliki tekstowe kończą się znakiem nowej linii. Dlatego nigdy nie czytasz linii bez znaku nowej linii na końcu, chyba że plik jest wypchany. To jest podwójne dla plików takich jak te w/etc. :-) –

+0

Po prostu ciekawy, kotlet nie odnosi się do niczego w szczególności. Czy w tym przypadku instalujesz plik w programie? – ray

0

Ponieważ promień zapytał, pomyślałem, że przepisałbym cały mój program bez użycia implikatury Perla (z wyjątkiem użycia <ARGV>, którą trudno napisać ręcznie). Prawdopodobnie będzie to uczynić Python ludzie szczęśliwsi (szelki Nie naruszając :-P):

while (my $line = <ARGV>) { 
    chop $line; 
    $line =~ s/#.*//; 
    next unless $line =~ /\S/; 
    @fields = (split ' ', $line)[0,1]; 
    print join(' ', @fields), "\n"; 
} 

Czy jest coś brakowało mi? Mam nadzieję, że nie. Uchwyt pliku ARGV jest specjalny. Powoduje, że każdy nazwany plik w wierszu poleceń jest czytany, chyba że nie jest określony, w takim przypadku odczytuje standardowe wejście.

Edytuj: Och, zapomniałem. split ' ' jest również magiczny, w przeciwieństwie do split//. Ta ostatnia po prostu pasuje do przestrzeni. Pierwsza dopasowuje dowolną liczbę białych znaków. To magiczne zachowanie jest używane domyślnie, jeśli nie podano wzoru dla split. (Niektórzy twierdzą, ale co /\s+/? ' ' i /\s+/ są podobne, z wyjątkiem jak spacje na początku linii jest traktowany. Więc ' ' naprawdę jest magiczne.)

Morał z tej historii jest taki, Perl jest świetny, jeśli lubisz magiczne zachowanie. Jeśli nie masz paska, użyj Pythona. :-P

+0

chomp zamiast siekać, na wypadek, gdyby w ostatniej linii pliku brakowało nowej linii – ysth

+0

ysth: Pytanie dotyczy Unixa, o ile wiem, a pliki tekstowe Unix mają zawsze kończyć się znakiem nowej linii. podobno w/etc. –

+0

Używałem religijnie chomp, przez wiele lat, ale doszedłem do wniosku, że jest to niepotrzebne (dla wielu celów) i dlatego właśnie jest tam kotlet. –

0

znaleźć na n-ty znak w MTH linia nr L --- przykładu dla znalezienia tabliczki


@echo off 

REM Next line = Set command value to a file OR Just Choose Your File By Skipping The Line 
vol E: > %temp%\justtmp.txt 
REM Vol E: = Find Volume Lable Of Drive E 

REM Next Line to choose line line no. +0 = line no. 1 
for /f "usebackq delims=" %%a in (`more +0 %temp%\justtmp.txt`) DO (set findstringline=%%a& goto :nextstep) 

:nextstep 

REM Next line to read nth to mth Character here 22th Character to 40th Character 
set result=%findstringline:~22,40% 

echo %result% 
pause 
exit /b 

Zapisz znaleźć label.cmd

wynik będzie Twój Etykieta dysku E

Ciesz się

Powiązane problemy