Je n'étais pas au courant de cette récompense jusqu'à aujourd'hui, lorsqu'un débutant a essayé de me coller l'UUOC sur le dos pour l'une de mes réponses. Il s'agissait d'une cat file.txt | grep foo | cut ... | cut ...
. Je lui ai fait part de mes réflexions et ce n'est qu'après avoir consulté le lien qu'il m'a donné concernant les origines du prix et la pratique en la matière. Une recherche plus approfondie m'a conduit à cette question. Malheureusement, en dépit d'une réflexion approfondie, aucune des réponses n'incluait mon raisonnement.
Je n'avais pas l'intention d'être sur la défensive en l'éduquant. Après tout, dans mes jeunes années, j'aurais écrit la commande comme suit grep foo file.txt | cut ... | cut ...
parce qu'à chaque fois que l'on fait le single fréquent grep
vous apprenez l'emplacement de l'argument du fichier et vous savez déjà que le premier est le modèle et que les suivants sont des noms de fichiers.
C'est en toute connaissance de cause que j'ai répondu à la question par la formule cat
préfixe en partie pour une raison de "bon goût" (selon les termes de Linus Torvalds), mais surtout pour une raison impérieuse de fonction.
Cette dernière raison étant plus importante, je l'exposerai en premier. Lorsque je propose un pipeline comme solution, je m'attends à ce qu'il soit réutilisable. Il est tout à fait probable qu'un pipeline soit ajouté à la fin d'un autre pipeline ou qu'il s'y greffe. Dans ce cas, le fait d'avoir un argument fichier pour grep gêne la réutilisation, et peut très bien le faire en silence sans message d'erreur si l'argument fichier existe. I. e. grep foo xyz | grep bar xyz | wc
vous donnera le nombre de lignes dans xyz
contenir bar
alors que vous attendez le nombre de lignes qui contiennent à la fois foo
y bar
. Le fait de devoir modifier les arguments d'une commande dans un pipeline avant de l'utiliser est source d'erreurs. Si l'on ajoute à cela la possibilité d'échecs silencieux, cette pratique devient particulièrement insidieuse.
La première raison n'est pas sans importance non plus, car une grande partie du "bon goût" n'est qu'une justification intuitive et subconsciente de choses telles que les échecs silencieux mentionnés ci-dessus, auxquels vous ne pouvez pas penser au moment où une personne ayant besoin d'éducation dit "mais ce chat n'est pas inutile".
Cependant, j'essaierai de rendre consciente la raison de "bon goût" que j'ai mentionnée précédemment. Cette raison est liée à l'esprit de conception orthogonale d'Unix. grep
n'est pas cut
y ls
n'est pas grep
. Par conséquent, au minimum grep foo file1 file2 file3
va à l'encontre de l'esprit du design. La manière orthogonale de procéder est la suivante cat file1 file2 file3 | grep foo
. Maintenant, grep foo file1
est simplement un cas particulier de grep foo file1 file2 file3
Si vous ne le traitez pas de la même manière, vous utilisez au moins des cycles d'horloge cérébrale en essayant d'éviter le prix du chat inutile.
Cela nous amène à l'argument selon lequel grep foo file1 file2 file3
est une concaténation, et cat
concatène, il convient donc de cat file1 file2 file3
mais parce que cat
n'est pas concaténée dans cat file1 | grep foo
Par conséquent, nous violons l'esprit de l'accord de l'Union européenne. cat
et le tout puissant Unix. Si c'était le cas, Unix aurait besoin d'une commande différente pour lire la sortie d'un fichier et la recracher sur stdout (sans la paginer ou quoi que ce soit d'autre, mais en la recrachant purement sur stdout). On se retrouverait donc dans la situation où l'on dirait cat file1 file2
ou vous dites dog file1
et se rappeler consciencieusement d'éviter cat file1
pour éviter de recevoir la récompense, tout en évitant dog file1 file2
puisqu'il faut espérer que la conception de dog
génère une erreur si plusieurs fichiers sont spécifiés.
J'espère qu'à ce stade, vous comprenez que les concepteurs d'Unix n'ont pas inclus de commande distincte pour cracher un fichier sur stdout, et qu'ils ont nommé cat
pour concaténer plutôt que de lui donner un autre nom. <edit>
il existe un tel chien, le malheureux <
de l'opérateur. Il est regrettable qu'il soit placé à la fin du pipeline, ce qui l'empêche d'être facilement composable. Il n'y a pas de moyen syntaxique ou esthétique de le placer au début. Il est également regrettable qu'il ne soit pas assez général pour que vous commenciez avec le chien, mais que vous puissiez simplement ajouter un autre nom de fichier si vous souhaitez qu'il soit traité après le précédent. (Le >
n'est pas aussi mauvais. Il est presque parfaitement placé à la fin. Il ne s'agit généralement pas d'une partie réutilisable d'un pipeline, et c'est pourquoi il est distingué symboliquement). </edit>
La question suivante est de savoir pourquoi il est important d'avoir des commandes qui crachent simplement un fichier ou la concaténation de plusieurs fichiers sur stdout, sans aucun autre traitement ? L'une des raisons est d'éviter que chaque commande Unix opérant sur l'entrée standard sache comment analyser au moins un argument de fichier de la ligne de commande et l'utiliser comme entrée s'il existe. La seconde raison est d'éviter que les utilisateurs aient à se rappeler : (a) où vont les arguments de nom de fichier ; et (b) d'éviter le bogue du pipeline silencieux mentionné ci-dessus.
Cela nous amène à la question de savoir pourquoi grep
dispose d'une logique supplémentaire. L'objectif est de permettre à l'utilisateur de faire preuve de fluidité dans l'utilisation des commandes qui sont utilisées fréquemment et sur une grande échelle. autonome (plutôt que comme un pipeline). Il s'agit d'un léger compromis d'orthogonalité pour un gain significatif de convivialité. Toutes les commandes ne doivent pas être conçues de cette manière et les commandes qui ne sont pas fréquemment utilisées doivent éviter complètement la logique supplémentaire des arguments de fichier (rappelez-vous que la logique supplémentaire entraîne une fragilité inutile (la possibilité d'un bogue)). L'exception est d'autoriser les arguments de fichier comme dans le cas de grep
. (à ce propos, notez que ls
a une raison complètement différente de ne pas seulement accepter, mais plutôt d'exiger des arguments de fichier)
Enfin, ce qui aurait pu être mieux fait, c'est que des commandes exceptionnelles telles que grep
(mais pas nécessairement ls
) génère une erreur si l'entrée standard est disponible. Ceci est raisonnable car les commandes incluent une logique qui viole l'esprit orthogonal du tout-puissant Unix pour le confort de l'utilisateur. Pour plus de commodité, c'est-à-dire pour éviter les souffrances causées par un échec silencieux, ces commandes ne devraient pas hésiter à violer leur propre violation en alertant l'utilisateur s'il y a une possibilité d'échec silencieux.