Webbit Graffiti

Breve racconto di formazione, nel quale un giovane monger ingenuo impara che 10 minuti di biblioteca possono salvare da 10 giorni di laboratorio, e che l'uomo di mondo sa quando tenere acceso il telefono

Me ne stavo all'ombra del totem dei Mongers, a fare cose che non ricordo. Sospetto che avessero a che fare con il tentare di far funzionare la rete. Squilla il telefono, è valdez. "Senti Stefano, qui c'è bisogno di uno oneliner al volo. Puoi venire?". Mi dà le coordinate e parto, rimuginando.

A sentire la parola "oneliner" mi vien freddo. So bene che l'advocacy standard impone di dire cose tipo "Perl è potente perché ci puoi fare anche gli oneliner", ma di fatto io non ho un bel rapporto con essi, e solitamente preferisco fare uno script di 3-4 righe ad hoc, usa e getta. Non tutti son capaci di fare i distillati. Non a caso esiste il Jack Daniel's ma anche il Laphroaig, e uno magari si accorge della differenza solo la mattina dopo. Ma sto divagando. Quel che voglio dire, in estrema sintesi, è che secondo me gli oneliner son tutt'altro che banali da scrivere: una riga sola, ma che riga! Questo e altro mi turbava, durante il tragitto fino al padiglione 7.

Arrivo allo stand, dove vengo presentato al direttore della rivista e a un ragazzo che immagino sia un sysadmin/webmaster/programmatore, o qualche figura simile. Ha il non comune pregio di esporre il problema in maniera succinta e chiara: "Come faccio a stampare, di un file di testo, solo le righe successive a quella in cui una certa stringa è stata trovata?" bepi ha già fatto un piccolo script, codificato all'istante. Farlo con uno oneliner però non è altrettanto facile. Valdez ha trovato un buon metodo per non pisciare in compagnia e al tempo stesso non fare la figura né del ladro né della spia: "Ah, o sono applicazioni web o io non tocco la tastiera". Inattaccabile.

Non so perché ma la prima idea (sbagliata) che mi si installa nella mente è quella che comporta l'uso di blocchi BEGIN e/o END. In generale il trucco è molto carino, e serve a capire un aspetto importante del ciclo di vita di uno script Perl: il blocco BEGIN viene eseguito non appena esso viene compilato. E quindi una volta sola. Lo stesso vale per il blocco END, con la differenza che viene eseguito alla fine. Ad esempio (ma non c'entra niente con il problema in esame) ecco un modo per stampare il numero di righe di un file, nel caso un cataclisma avesse spazzato via dalla Terra il comando wc:

perl -ne '++$counter; END { print $counter }' test.txt

È però una strada sterile, e infatti non si riesce a cavarne una soluzione. Nel frattempo il ragazzo dello stand continua a dire "Eppure pensavo che fosse una cosa banale!" e ogni volta è una pugnalata nel petto, petto che, guarda caso, in questo momento è coperto da una felpa con la scritta "Perl", e insomma non è certo l'abito adatto per fare simili brutte figure.

A questo punto, sarà la pressione, sarà il Laphroaig, sarà quel che sarà, ma mi viene in mente uno degli operatori più bizzarri del Perl: l'operatore punto-punto. Calma: in un contesto di lista è semplice, quasi banale: (1 .. 10) vuol dire "la lista dei numeri da 1 a 10", ed è chiaro e lampante. In un contesto scalare, le cose cominciano a complicarsi. Il valore dell'espressione che contiene l'operatore .. è falso finché l'operando di sinistra è falso, poi diventa vero e lo rimane, fino a quando anche l'operando di destra è falso. Questa cosa però non me la ricordavo bene, e volevo controllare la documentazione.

"Mi fate dare un'occhio alla documentazione?" "No beh, dai ragazzi, pure leggere il manuale, pensavo fosse una cosa banale…" Mi sento mancare.

Com'è come non è, riesco ad impossessarmi della tastiera, e senza chiedere il permesso digito perldoc perlop, poi tocco un paio di punti di pressione e localizzo le informazioni che mi servono. Ti ho beccato, dannato punto-punto! Ecco la soluzione:

perl -ne 'print if /foo/ .. 1' test.txt

Facile no? Finché non si incontra "foo" dentro ad una riga, l'espressione è falsa e quindi non si stampa niente, poi si stampa fino alla fine del file, visto che la costante 1 ha valore di verità vero. Been there, done that! E invece no! Perchè non si vuole stampare la riga che soddisfa la RE. Sursum corda, la modifica è semplice:

perl -ne 'print if /foo/ .. 1 and !/foo/' test.txt

La morale? Ah perché, deve esserci pure una morale?

Comments

by dada, 2004-05-14

nonostante sia molto carina la tua soluzione (e delizioso il racconto :-), a me non piace l'idea di dover ripetere 2 volte 'foo' sulla stessa riga, soprattutto se il foo in questione può essere arbitrariamente complesso. preferisco quindi questa versione più sporca:

perl -ne 'print if $x; $x=1 if /foo/' test.txt

la morale? Tim Toady, ovviamente :-) cheers,

Aldo

by dada, 2004-05-14

altro appunto: la prima strada da te battuta non è sterile:

perl -ne 'last if /foo/; END { print <> }' test.txt

vuoi dunque un'altra morale? don't give up too easily :-) cheers,

Aldo

by Lucas, 2004-05-14

Piccola variante alle precedenti, con vantaggio di avre un unico statement e raccogliere il numero delle righe successive a foo in $r.

perl -ne '$r++ if /foo/ || $r && print' test.txt

morale? TMTOWTDI

by marco, 2004-05-15

perl -ne 'BEGIN{$/=foo;>} print' test.txt

TMTOWTDI :)

un saluto a tutti

P.S. Non sarei stato in grado di inventarmela li' per li'

by marco, 2004-05-15

ops..

perl -ne 'BEGIN{$/=foo;<>} print' test.txt

by dada, 2004-05-20

marco, mi permetto di dissentire.

  • il tuo 'foo' non è un pattern, ma una stringa (il che può anche andare bene per come era stato posto il problema, ma è un punto di differenza rispetto agli altri oneliner)
  • ma soprattutto, il tuo script funziona solo nel caso in cui la riga contiene solo la stringa incriminata (o meglio, contiene la stringa come ultima cosa della riga)

se il file test.txt contenesse ad esempio:

questa riga non compare
quest'altra nemmeno
questa contiene foo, ma non dovrebbe comparire
questa riga compare
quest'altra pure

l'output del tuo script è:

, ma non dovrebbe comparire
questa riga compare
quest'altra pure

la prima riga dell'output non ci dovrebbe essere! cheers, Aldo

by marco, 2004-05-20

Vero :( chiedo venia :)

Marco

by marco, 2004-05-20

Allora, tanto per per fare un'altra figuraccia :)

perl -ne 's/foo/print<>;exit/e' test.txt

by Lucas, 2004-05-21

Rieccomi, devo dire che ho trovato un nuovo passatempo! l'argomento mi affascina ;) e a quanto sembra non solo a me.

La prima soluzione data da Larsen mi era piaciuta per il modo inconsueto di usare l'operatore range (..) così mi è tornato in mente l'altro operatore range (...)

perl -ne '/foo/...print && 0' test.txt

In effetti quest'ultimo fa proprio al caso nostro in quanto in virtù della sua caratteristica, effettua un ulteriore ciclo prima di valutare l'operatore di dx che inizia a stampare.

morale? bastaaa!!!

[LucaS]

Author: Stefano Rodighiero

Created: 2024-03-05 Tue 09:01

Validate