Non ero a conoscenza del premio fino ad oggi quando un novellino ha cercato di appuntare l'UUOC su di me per una delle mie risposte. Era un cat file.txt | grep foo | cut ... | cut ...
. Gli ho dato un pezzo della mia mente, e solo dopo aver visitato il link mi ha fatto riferimento alle origini del premio e alla pratica di farlo. Ulteriori ricerche mi hanno portato a questa domanda. Un po 'sfortunatamente, nonostante la considerazione consapevole, nessuna delle risposte includeva la mia logica.
Non intendevo essere difensivo quando lo educavo. Dopotutto, nei miei anni più giovani avrei scritto il comando come grep foo file.txt | cut ... | cut ...
perché ogni volta che fai i singoli frequenti grep
impari il posizionamento dell'argomento file ed è subito noto che il primo è lo schema e quelli successivi sono i nomi dei file.
È stata una scelta consapevole quando ho risposto alla domanda con il cat
prefisso in parte a causa di un motivo di "buon gusto" (nelle parole di Linus Torvalds) ma principalmente per un motivo convincente di funzione.
Quest'ultimo motivo è più importante, quindi lo pubblicherò per primo. Quando offro una pipeline come soluzione, mi aspetto che sia riutilizzabile. È molto probabile che una pipeline venga aggiunta alla fine o giunta in un'altra pipeline. In tal caso, avere un argomento file per grep rovina la riusabilità e molto probabilmente lo fa silenziosamente senza un messaggio di errore se esiste l'argomento file. I. e. grep foo xyz | grep bar xyz | wc
ti fornirà quante righe xyz
contengono bar
mentre ti aspetti il numero di righe che contengono sia foo
e bar
. Dover cambiare argomenti in un comando in una pipeline prima di usarlo è soggetto a errori. Aggiungi ad esso la possibilità di fallimenti silenziosi e diventa una pratica particolarmente insidiosa.
La prima ragione non è irrilevante, poiché un sacco di "buon gusto" è semplicemente una logica subconscia intuitiva per cose come i silenziosi fallimenti di cui non si può pensare proprio nel momento in cui una persona bisognosa di istruzione dice "ma non lo è quel gatto inutile ".
Cercherò comunque di rendere consapevole anche l'ex "buon gusto" che ho citato. Questa ragione ha a che fare con lo spirito progettuale ortogonale di Unix. grep
non lo fa cut
e ls
non lo fa grep
. Quindi almeno grep foo file1 file2 file3
va contro lo spirito del design. Il modo ortogonale di farlo è cat file1 file2 file3 | grep foo
. Ora, grep foo file1
è semplicemente un caso speciale di grep foo file1 file2 file3
, e se non lo tratti allo stesso modo, stai almeno utilizzando cicli di clock del cervello che cercano di evitare l'inutile premio del gatto.
Questo ci porta all'argomentazione che grep foo file1 file2 file3
sta concatenando e cat
concatena, quindi è giusto, cat file1 file2 file3
ma poiché cat
non si sta concatenando, cat file1 | grep foo
quindi stiamo violando lo spirito sia dell'uno cat
che dell'onnipotente Unix. Bene, se così fosse, allora Unix avrebbe bisogno di un comando diverso per leggere l'output di un file e sputarlo su stdout (non impaginarlo o altro solo uno sputo puro su stdout). Quindi avresti la situazione in cui dici cat file1 file2
o dici dog file1
e ricordi coscienziosamente di evitare cat file1
di evitare di ottenere il premio, evitando anche dog file1 file2
dal momento che si spera che la progettazione di dog
avrebbe generato un errore se vengono specificati più file.
Spero che a questo punto simpatizzi con i progettisti Unix per non aver incluso un comando separato per sputare un file su stdout, nominando anche cat
concatenare invece di dargli un altro nome. <edit>
c'è un cane del genere, lo sfortunato <
operatore. È sfortunato il suo posizionamento alla fine della pipeline che impedisce una facile componibilità. Non esiste un modo sintatticamente o esteticamente pulito di posizionarlo all'inizio. È anche sfortunato non essere abbastanza generale, quindi inizi con il cane ma aggiungi semplicemente un altro nome file se vuoi che sia elaborato dopo quello precedente. ( >
D'altra parte non è male la metà. Ha un posizionamento quasi perfetto alla fine. In genere non è una parte riutilizzabile di una tubazione, e di conseguenza si distingue simbolicamente.)</edit>
La prossima domanda è: perché è importante avere comandi che sputano semplicemente un file o la concatenazione di più file su stdout, senza ulteriori elaborazioni? Un motivo è quello di evitare di avere ogni singolo comando Unix che opera su input standard per sapere come analizzare almeno un argomento del file della riga di comando e usarlo come input se esiste. Il secondo motivo è evitare che gli utenti debbano ricordare: (a) dove vanno gli argomenti del nome file; e (b) evitare il bug della pipeline silenziosa come menzionato sopra.
Questo ci porta al perché grep
ha la logica in più. La logica è consentire la fluidità dell'utente per i comandi che vengono utilizzati frequentemente e su base autonoma (anziché come pipeline). È un leggero compromesso dell'ortogonalità per un significativo guadagno nell'usabilità. Non tutti i comandi dovrebbero essere progettati in questo modo e i comandi che non vengono utilizzati di frequente dovrebbero evitare completamente la logica aggiuntiva degli argomenti dei file (ricordare che la logica aggiuntiva porta a una fragilità non necessaria (la possibilità di un bug)). L'eccezione è consentire argomenti di file come nel caso di grep
. (a proposito, nota che ls
ha un motivo completamente diverso per non solo accettare ma praticamente richiedere argomenti sui file)
Infine, ciò che avrebbe potuto essere fatto meglio è se comandi eccezionali come grep
(ma non necessariamente ls
) generano un errore se è disponibile l'input standard. Ciò è ragionevole perché i comandi includono una logica che viola lo spirito ortogonale dell'onnipotente Unix per comodità dell'utente. Per ulteriore comodità dell'utente, vale a dire per prevenire la sofferenza causata da un guasto silenzioso, tali comandi non dovrebbero esitare a violare la propria violazione avvisando l'utente se esiste la possibilità di un guasto silenzioso.