Perl: afferrare l'ennesima e la delimitata parola da ogni riga in un file
Domanda
A causa del modo più noioso di aggiungere host da monitorare in Nagios (richiede la definizione di un oggetto host, al contrario del programma precedente che richiedeva solo l'IP e il nome host), ho pensato che sarebbe stato meglio automatizzare questo , e sarebbe un ottimo momento per imparare il Perl, perché tutto quello che so al momento è C / C ++ e Java.
Il file da cui ho letto è simile al seguente:
xxx.xxx.xxx.xxx hostname #comments. i.dont. care. about
Tutto quello che voglio sono i primi 2 mazzi di personaggi. Questi sono ovviamente delimitati da spazi, ma per motivi di generalità, potrebbe anche essere qualsiasi cosa. Per renderlo più generale, perché non il primo e il terzo, o il quarto e il decimo? Sicuramente ci deve essere qualche azione regex, ma per il momento lascerò quel tag disattivato, per ogni evenienza.
Soluzione
Il one-liner è fantastico, se non stai scrivendo più Perl per gestire il risultato.
Più in generale, tuttavia, nel contesto di un programma Perl più ampio, potresti scrivere un'espressione regolare personalizzata, ad esempio:
if($line =~ m/(\S+)\s+(\S+)/) {
$ip = $1;
$hostname = $2;
}
... o useresti l'operatore split .
my @arr = split(/ /, $line);
$ip = $arr[0];
$hostname = $arr[1];
Ad ogni modo, aggiungi la logica per verificare l'input non valido.
Altri suggerimenti
Trasformiamolo in codice golf! Sulla base dell'eccellente risposta di David, ecco la mia:
perl -ane 'print "@F[0,1]\n";'
Modifica: un vero invio di golf sarebbe più simile a questo (rasatura di cinque colpi):
perl -ape '$_="@F[0,1]
"'
ma è meno leggibile ai fini di questa domanda. :-P
Ecco una soluzione generale (se ci allontaniamo un po 'dal gioco del codice).
#!/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";
Normalmente split
si divide per spazio bianco. Se non è quello che vuoi (ad esempio, analizzando /etc/passwd
), puoi passare un delimitatore come regex:
@fields = (split /:/)[0,2,4..6];
Naturalmente, se stai analizzando i file delimitati da due punti, è anche probabile che tali file non abbiano commenti e non devi rimuoverli.
Un semplice one-liner è
perl -nae 'print "$F[0] $F[1]\n";'
puoi modificare il delimitatore con -F
David Nehme ha detto:
perl -nae 'print "$F[0] $F[1}\n";
che utilizza l'opzione -a
. Ho dovuto cercarlo:
-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.
impari qualcosa ogni giorno. -n causa il passaggio di ciascuna riga a
LINE:
while (<>) {
... # your program goes here
}
E infine -e
è un modo per inserire direttamente una singola riga di un programma. Puoi avere più di perlrun(1)
. Gran parte di questo è stato uno strappo della <=> manpage.
Da quando Ray ha chiesto, ho pensato di riscrivere il mio intero programma senza usare l'implicito di Perl (tranne l'uso di <ARGV>
; è difficile da scrivere a mano). Questo probabilmente renderà le persone Python più felici (nonostante le parentesi graffe :-P):
while (my $line = <ARGV>) {
chop $line;
$line =~ s/#.*//;
next unless $line =~ /\S/;
@fields = (split ' ', $line)[0,1];
print join(' ', @fields), "\n";
}
C'è qualcosa che mi sono perso? Spero di no. Il ARGV
filehandle è speciale. Fa sì che ogni file con nome sulla riga di comando venga letto, a meno che non sia specificato nessuno, nel qual caso legge l'input standard.
Modifica: Oh, ho dimenticato. Anche split ' '
è magico, a differenza di split / /
. Quest'ultimo corrisponde solo a uno spazio. Il primo corrisponde a qualsiasi quantità di qualsiasi spazio bianco. Questo comportamento magico viene utilizzato per impostazione predefinita se non viene specificato alcun motivo per split
. (Alcuni direbbero che ma che dire di /\s+/
? ' '
e <=> sono simili, tranne per il modo in cui viene trattato lo spazio bianco all'inizio di una linea. Quindi <=> è davvero magico. )
La morale della storia è che Perl è fantastico se ti piacciono i comportamenti magici. Se non ne hai una barra, usa Python. :-P
Per trovare il carattere da nono all'ottavo nella riga n. L --- Esempio di ricerca dell'etichetta
@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
Salva come trova label.cmd
Il risultato sarà la tua etichetta E Drive
Godetevi