2009-07-14 2 views
5

Oggi ho ricevuto questa strana riga di codice, mi dice "vuoto" o "non vuoto" a seconda che nella CWD siano presenti elementi (diversi da . e ..).Come funziona questo rivestimento Perl one per controllare se una directory è vuota?

Voglio sapere come funziona perché non ha senso per me.

perl -le 'print+(q=not =)[2==(()=<.* *>)].empty' 

Il bit Sono interessato a è <.* *>. Non capisco come ottiene i nomi di tutti i file nella directory.

+0

Sentiti libero di modificare i tag per qualcosa di più appropriato – dsm

+3

Haha, sei così divertente! Sto rotolando sul pavimento ridendo. Hai chiamato il rumore della linea Perl !!!! HAHAHAHAHAHAHAHAHAHAHAHAHA! – jrockway

+0

Bella presentazione per "offuscamento dell'anno". Mi piace Perl, ma mi piacciono ancora di più i commenti in codice Perl. – Boldewyn

risposta

16

È una fodera da golf. Il flag -e significa eseguire il resto della riga di comando come programma. -l consente l'elaborazione automatica della fine della linea.

Il <.* *> porzione è un glob contenente due pattern di espansione: .* e *.

Questa porzione

(q=not =) 

è una lista che contiene un singolo valore - la stringa "no". Il numero q=...= è un delimitatore di stringa alternativo, apparentemente utilizzato perché la virgoletta singola viene utilizzata per citare il segnaposto.

La parte [...] è il pedice in tale elenco. Il valore del pedice sarà o 0 (il valore "non  ") o 1 (niente, che stampa la stringa vuota) a seconda del risultato di questo confronto:

2 == (()=<.* *>) 

C'è un sacco accadendo qui. Il confronto verifica se il glob ha restituito o meno un elenco di esattamente due articoli (supponendo che siano . e ..), ma come farlo è complicato. Le parentesi interne indicano una lista vuota. Assegnare a questo elenco mette il glob nel contesto dell'elenco in modo che restituisca tutti i file nella directory. (In contesto scalare si comporterebbe come un iteratore e restituirà solo uno alla volta.) L'assegnazione stessa viene valutata in contesto scalare (essendo sul lato destro del confronto) e quindi restituisce il numero di elementi assegnati.

Il numero iniziale + impedisce a Perl di analizzare l'elenco come argomenti su print.Il trailing .empty concatena la stringa "vuota" a qualsiasi cosa uscisse dall'elenco (ad esempio "non  " o la stringa vuota).

+1

+1 bella spiegazione concisa – hillu

+0

Questo è un po 'fuorviante. Il inner() lo rende un assegnamento di lista, che dà il suo giusto contesto di lista di operandi, ma potrebbe anche essere (2 == (@ a = <.* *>)). – ysth

+0

@ysth: Non sono sicuro del perché pensi che sia fuorviante. Dato che non c'è niente nella parte interna '()' la lista viene scartata. Lo scopo della parentesi (dal punto di vista del programmatore) era di imporre il contesto dell'elenco su glob. Avrebbe potuto assegnarlo a un array (che sarebbe stato più chiaro a IMHO), ma non lo fece. O ti stai riferendo al fatto che l'assegnazione vuota cambia la semantica da "una lista in contesto scalare restituisce l'ultimo elemento" a "elenco assegnazione restituisce il numero di elementi"? Probabilmente dovrei modificare la risposta per chiarire quel bit ... –

3

<.* *> significa (glob(".*"), glob("*")). glob espande i pattern di file nello stesso modo in cui lo fa la shell.

7
<.* *> 

è un glob composta da due modelli: .* sono tutti i nomi di file che iniziano con . e * corrisponde a tutti i file (questo è diverso rispetto alle solite convenzioni DOS/Windows).

(()=<.* *>) 

valuta il glob nel contesto dell'elenco, restituendo tutti i nomi di file corrispondenti.

Quindi, il confronto con 2 lo inserisce in un contesto scalare in modo che 2 venga confrontato con il numero di file restituiti. Se quel numero è 2, le sole voci della directory sono . e .., periodo. ;-)

0

La documentazione relativa a tale funzione è here. (Scorrere verso la fine della sezione)

+2

Quale caratteristica? Questo dovrebbe essere un commento alla risposta di qualcuno? – Telemachus

+0

@Telemachus: <> per indicare glob, presumibilmente. – ysth

+0

@Telemachus: la documentazione per il glob/<>, come @ysth menzionata – dsm

2

Trovo che il modulo B::Deparse aiuta un po 'in decifrare alcune cose che getta via gli occhi la maggior parte dei programmatori, come ad esempio il q=...= costrutto:

$ perl -MO=Deparse,-p,-q,-sC 2>/dev/null << EOF 
> print+(q=not =)[2==(()=<.* *>)].empty 
> EOF 
use File::Glob(); 
print((('not ')[(2 == (() = glob('.* *')))] . 'empty')); 

Naturalmente , questo non produce immediatamente codice "leggibile", ma sicuramente converte alcuni degli ostacoli.

+0

Heh. Nessuna quantità di deparsing vanificherà l'uso di un booleano da indicizzare in una lista anonima. [anti-pedanteria: Sì, so che "elenco anonimo" è ridondante.] –