C'est probablement dans de nombreuses FAQ - au lieu d'utiliser:
cat file | command
(qui s'appelle l'utilisation inutile de chat), manière correcte supposée être:
command < file
Dans la deuxième manière, «correcte» - OS n'a pas à engendrer un processus supplémentaire.
Bien que sachant cela, j'ai continué à utiliser un chat inutile pour 2 raisons.
plus esthétique - j'aime quand les données ne se déplacent uniformément que de gauche à droite. Et il est plus facile de remplacer
cat
par autre chose (gzcat
,echo
...), ajoutez un 2ème fichier ou insérer un nouveau filtre (pv
,mbuffer
,grep
...).J'ai «senti» que cela pourrait être plus rapide dans certains cas. Plus rapide car il y a 2 processus, 1st (
cat
) fait la lecture et le second fait tout. Et ils peuvent fonctionner en parallèle, ce qui signifie une exécution parfois plus rapide.
Ma logique est-elle correcte (pour la 2ème raison)?
la source
cat
est un canal d'identité . Il diffuse uniquement son entrée vers sa sortie. Si le deuxième programme de la chaîne peut prendre son entrée du même argument que vous passez àcat
(ou de l'entrée standard, si vous ne passez aucun argument), alorscat
est absolument inutile et ne fait qu'un processus supplémentaire étant forké et un tube supplémentaire étant établi.-
, c'est un tuyau d'identité. Quand il a plus d'un argument de nom de fichier sans tiret, il devient quelque chose de plus qu'un canal d'identité, cependant, et commence à servir un objectif réel.<file command1 | command2
, bien qu'il y ait un désaccord sur l'esthétique.Réponses:
Je n'étais pas au courant de la récompense jusqu'à aujourd'hui, quand une recrue a essayé de m'épingler l' UUOC pour l'une de mes réponses. C'était un
cat file.txt | grep foo | cut ... | cut ...
. Je lui ai donné une idée de mon esprit et ce n'est qu'après avoir visité le lien qu'il m'a donné en me référant aux origines du prix et à la pratique de le faire. Des recherches plus poussées m'ont conduit à cette question. Un peu malheureusement malgré un examen conscient, aucune des réponses ne comprenait ma justification.Je n'avais pas voulu être sur la défensive en lui répondant. Après tout, dans mes jeunes années, j'aurais écrit la commande comme
grep foo file.txt | cut ... | cut ...
parce que chaque fois que vous faites les simples simples,grep
vous apprenez le placement de l'argument de fichier et il est prêt à savoir que le premier est le modèle et les derniers sont les noms de fichiers.C'était un choix conscient à utiliser
cat
lorsque j'ai répondu à la question, en partie pour une raison de «bon goût» (selon les mots de Linus Torvalds) mais principalement pour une raison impérieuse de fonction.Cette dernière raison est plus importante, je vais donc l'exposer en premier. Lorsque j'offre un pipeline comme solution, je m'attends à ce qu'il soit réutilisable. Il est fort probable qu'un pipeline soit ajouté à la fin ou épissé dans un autre pipeline. Dans ce cas, avoir un argument de fichier pour grep gâche la réutilisabilité, et peut-être le faire silencieusement sans message d'erreur si l'argument de fichier existe. C'est à dire.
grep foo xyz | grep bar xyz | wc
vous indiquera le nombre de lignesxyz
contenuesbar
pendant que vous attendez le nombre de lignes contenant à la foisfoo
etbar
. Le fait de devoir modifier les arguments d'une commande dans un pipeline avant de l'utiliser est sujet aux erreurs. Ajoutez à cela la possibilité d'échecs silencieux et cela devient une pratique particulièrement insidieuse.La première raison n'est pas sans importance non plus car beaucoup de " bon goût " est simplement une justification subconsciente intuitive pour des choses comme les échecs silencieux ci-dessus auxquels vous ne pouvez pas penser correctement au moment où une personne ayant besoin d'éducation dit "mais n'est pas ce chat inutile ".
Cependant, j'essaierai également de rendre conscient l'ancienne raison de «bon goût» que j'ai mentionnée. Cette raison a à voir avec l'esprit de conception orthogonale d'Unix.
grep
ne le fait pascut
etls
ne le fait pasgrep
. Par conséquent, à tout le moinsgrep foo file1 file2 file3
va à l'encontre de l'esprit du design. La manière orthogonale de le faire estcat file1 file2 file3 | grep foo
. Maintenant,grep foo file1
c'est simplement un cas particulier degrep foo file1 file2 file3
, et 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 qui
grep foo file1 file2 file3
concatène etcat
concatène donc c'est propre,cat file1 file2 file3
mais parce que cecat
n'est pas concaténant,cat file1 | grep foo
nous violons donc l'esprit à la foiscat
du tout-puissant Unix. Eh bien, si tel était le cas, Unix aurait besoin d'une commande différente pour lire la sortie d'un fichier et le cracher sur stdout (pas le paginer ou quoi que ce soit juste un pur crachement vers stdout). Ainsi, vous auriez la situation où vous ditescat file1 file2
ou vous ditesdog file1
et rappelez-vous consciencieusement d'évitercat file1
d'éviter d'obtenir le prix, tout en évitant égalementdog file1 file2
car, espérons-le, la conception dedog
lèverait une erreur si plusieurs fichiers sont spécifiés.Espérons qu'à ce stade, vous sympathisez avec les concepteurs Unix pour ne pas inclure une commande séparée pour cracher un fichier sur stdout, tout en nommant
cat
pour concaténer plutôt que de lui donner un autre nom.<edit>
supprimé les commentaires incorrects sur<
, en fait,<
est une fonction efficace sans copie pour cracher un fichier sur stdout que vous pouvez positionner au début d'un pipeline afin que les concepteurs Unix aient inclus quelque chose spécifiquement pour cela</edit>
La question suivante est pourquoi est-il important d'avoir des commandes qui crachent simplement un fichier ou la concaténation de plusieurs fichiers vers stdout, sans autre traitement? Une des raisons est d'éviter d'avoir chaque commande Unix qui fonctionne sur une entrée standard pour savoir comment analyser au moins un argument de fichier de ligne de commande et l'utiliser comme entrée s'il existe. La deuxième raison est d'éviter aux utilisateurs d'avoir à se souvenir: (a) où vont les arguments du nom de fichier; et (b) éviter le bogue du pipeline silencieux comme mentionné ci-dessus.
Cela nous amène à pourquoi
grep
a la logique supplémentaire. La justification est de permettre à l'utilisateur de maîtriser les commandes fréquemment utilisées et de manière autonome (plutôt que sous forme de pipeline). C'est un léger compromis d'orthogonalité pour un gain significatif en ergonomie. Toutes les commandes ne doivent pas être conçues de cette façon et les commandes qui ne sont pas fréquemment utilisées doivent complètement éviter la logique supplémentaire des arguments de fichier (rappelez-vous que la logique supplémentaire conduit à une fragilité inutile (la possibilité d'un bogue)). L'exception est d'autoriser les arguments de fichier comme dans le cas degrep
. (Au fait, notez que celals
a une raison complètement différente non seulement d'accepter mais d'exiger à peu près des arguments de fichier)Enfin, ce qui aurait pu être mieux fait, c'est si des commandes exceptionnelles comme
grep
(mais pas nécessairementls
) génèrent une erreur si l'entrée standard est également disponible lorsque les arguments de fichier sont spécifiés.la source
grep
est appelé avec plusieurs noms de fichier, il préfixe les lignes trouvées avec le nom du fichier dans lequel il a été trouvé (sauf si vous désactivez ce comportement). Il peut également signaler les numéros de ligne dans les fichiers individuels. Si vous ne l'utilisez quecat
pour alimentergrep
, vous perdez les noms de fichiers et les numéros de ligne sont continus sur tous les fichiers, pas par fichier. Ainsi, il y a des raisons d'avoirgrep
gérer plusieurs fichiers lui-même quicat
ne peuvent pas gérer. Les cas de fichier unique et zéro fichier sont simplement des cas particuliers d'utilisation générale de fichiers multiplesgrep
.< file command1 ...
. Bien que la position conventionnelle des opérateurs de redirection d'E / S soit après le nom de la commande et ses arguments, ce n'est que la convention et non un placement obligatoire. Le<
doit précéder le nom du fichier. Donc, il y a une symétrie parfaite près de entre>output
et<input
Redirections:<input command1 -opt 1 | command2 -o | command3 >output
.cat
ne sert à rien. Ce n'est pas cela qui necat
sert à rien; c'est qu'une construction particulière n'a pas besoin d'être utiliséecat
. Si vous le souhaitez, notez que c'est UUoC (Useless Use ofcat
), et non UoUC (Use of Uselesscat
). Il existe de nombreuses occasions oùcat
est le bon outil à utiliser; Je n'ai aucun problème avec son utilisation quand c'est le bon outil à utiliser (et, en fait, je mentionne un cas dans ma réponse).cat
dans le tube peut ne pas être un gros problème en fonction des données, mais lorsqu'il est utilisé comme environnement de programmation, il peut être absolument nécessaire d'implémenter ces éléments critiques pour les performances; surtout lorsqu'il s'agit de savoirbash
qui, en termes de performances, est comme une roue de forme rectangulaire (par rapport à deksh
toute façon. Je parle jusqu'à 10 fois plus lentement ici - sans blague). Vous ne souhaitez optimiser vos fourches (et pas seulement) lorsqu'ils traitent avec de plus grands scripts ou des boucles énormes.Nan!
Tout d'abord, peu importe où dans une commande la redirection se produit. Donc, si vous aimez votre redirection vers la gauche de votre commande, c'est très bien:
est le même que
Deuxièmement, il y a n + 1 processus et un sous-shell qui se produisent lorsque vous utilisez un tube. C'est décidément plus lent. Dans certains cas, n aurait été zéro (par exemple, lorsque vous redirigez vers un shell intégré), donc en utilisant
cat
vous ajoutez un nouveau processus totalement inutilement.En général, chaque fois que vous vous retrouvez à utiliser un tuyau, cela vaut la peine de prendre 30 secondes pour voir si vous pouvez l'éliminer. (Mais cela ne vaut probablement pas la peine de prendre plus de 30 secondes.) Voici quelques exemples où les tuyaux et les processus sont fréquemment utilisés inutilement:
N'hésitez pas à modifier pour ajouter plus d'exemples.
la source
< cat grep dog
est un exemple artificiel pour montrer que vous ne pouvez pas facilement distinguer le fichier d'entrée, la commande qui reçoit l'entrée et les arguments de la commande.stdout=$(foo bar -exec baz <qux | ENV=VAR quux)
. Q. S'applique-t-il<qux
àfoo
, ou àbaz
, qui est-exec
'd byfoo
? R. Cela s'applique àfoo
, mais peut paraître ambigu. Mettre<qux
avantfoo
dans ce cas est plus clair, bien que moins courant, et est analogue à la finENV=VAR quux
.<"cat" grep dog
est plus facile à lire, là-bas. (Je suis généralement pro-espace, mais ce cas particulier est vraiment une exception).Je ne suis pas d'accord avec la plupart des exemples du prix UUOC excessivement suffisant car, lorsque vous enseignez à quelqu'un d'autre, il
cat
s'agit d'un espace réservé pratique pour toute commande ou pipeline compliqué et croustillant de commandes qui produisent une sortie adaptée au problème ou à la tâche discutée.Cela est particulièrement vrai sur des sites tels que Stack Overflow, ServerFault, Unix et Linux ou l'un des sites SE.
Si quelqu'un pose spécifiquement des questions sur l'optimisation ou si vous avez envie d'ajouter des informations supplémentaires à ce sujet, alors, bien, expliquez à quel point l'utilisation de chat est inefficace. Mais ne réprimandez pas les gens parce qu'ils ont choisi de viser la simplicité et la facilité de compréhension dans leurs exemples plutôt que de regarder-moi-comment-cool-suis-je! complexité.
Bref, parce que le chat n'est pas toujours un chat.
Aussi parce que la plupart des gens qui aiment décerner des UUOC le font parce qu'ils sont plus soucieux de montrer à quel point ils sont `` intelligents '' que d'aider ou d'enseigner les gens. En réalité, ils démontrent qu'ils sont probablement juste un autre débutant qui a trouvé un petit bâton avec lequel battre leurs pairs.
Mettre à jour
Voici un autre UUOC que j'ai publié dans une réponse à https://unix.stackexchange.com/a/301194/7696 :
Les pédants UUOC diraient que c'est un UUOC parce qu'il est facilement possible de faire
$filter
par défaut la chaîne vide et d'avoir l'if
instruction dofilter='| grep -v "^$"'
mais IMO, en n'incorporant pas le caractère pipe dans$filter
, ce "inutile"cat
sert le but extrêmement utile d'auto-documenter le fait qui$filter
sur laprintf
ligne n'est pas simplement un autre argumentsqlplus
, c'est un filtre de sortie optionnel sélectionnable par l'utilisateur.S'il y a besoin d'avoir plusieurs filtres de sortie en option, le traitement des options pourrait tout append
| whatever
à$filter
aussi souvent que nécessaire - un supplémentairecat
dans le pipeline ne va pas quoi que ce soit blessé ou causer une perte notable de performance.la source
==
intérieur[ ]
n'est pas spécifié par POSIX, et toutes les implémentations ne l'acceptent pas. L'opérateur standardisé est juste=
.Avec la version UUoC,
cat
il faut lire le fichier en mémoire, puis l'écrire dans le tube, et la commande doit lire les données du tube, le noyau doit donc copier le fichier entier trois fois alors que dans le cas de la redirection, le noyau n'a qu'à copier le fichier une seule fois. Il est plus rapide de faire quelque chose une fois que de le faire trois fois.En utilisant:
est une utilisation totalement différente et pas nécessairement inutile de
cat
. Il est toujours inutile si la commande est un filtre standard qui accepte zéro ou plusieurs arguments de nom de fichier et les traite à son tour. Considérez latr
commande: c'est un filtre pur qui ignore ou rejette les arguments de nom de fichier. Pour y alimenter plusieurs fichiers, vous devez utilisercat
comme indiqué. (Bien sûr, il y a une discussion distincte sur le fait que la conceptiontr
n'est pas très bonne; il n'y a aucune vraie raison pour laquelle elle n'aurait pas pu être conçue comme un filtre standard.) Cela peut également être valide si vous voulez que la commande traite toutes les entrées comme un un seul fichier plutôt que plusieurs fichiers séparés, même si la commande accepterait plusieurs fichiers séparés: par exemple,wc
est une telle commande.C'est le
cat single-file
cas qui est inconditionnellement inutile.la source
Pour la défense du chat:
Oui,
ou
est plus efficace, mais de nombreuses invocations n'ont pas de problèmes de performances, donc vous vous en fichez.
raisons ergonomiques:
Nous avons l'habitude de lire de gauche à droite, donc une commande comme
est trivial à comprendre.
doit sauter par-dessus process1, puis lire de gauche à droite. Cela peut être guéri par:
ressemble en quelque sorte, comme s'il y avait une flèche pointant vers la gauche, là où rien n'est. Plus déroutant et ressemblant à des citations sophistiquées, c'est:
et la génération de scripts est souvent un processus itératif,
où vous voyez vos progrès par étapes, tandis que
ne fonctionne même pas. Les méthodes simples sont moins sujettes aux erreurs et la caténation des commandes ergonomiques est simple avec cat.
Un autre sujet est que la plupart des gens ont été exposés à> et <en tant qu'opérateurs de comparaison, bien avant d'utiliser un ordinateur et lorsqu'ils utilisent un ordinateur en tant que programmeurs, y sont beaucoup plus souvent exposés en tant que tels.
Et comparer deux opérandes avec <et> est contra-commutatif, ce qui signifie
Je me souviens de la première fois que j'utilisais <pour la redirection d'entrée, j'avais peur
pourrait signifier la même chose que
et en quelque sorte écraser mon script a.sh. C'est peut-être un problème pour de nombreux débutants.
rares différences
Ce dernier peut être utilisé directement dans les calculs.
Bien sûr, le <peut être utilisé ici aussi, au lieu d'un paramètre de fichier:
mais qui s'en soucie - 15k?
Si je rencontrais parfois des problèmes, je changerais sûrement mon habitude d'invoquer le chat.
Lorsque vous utilisez des fichiers très volumineux ou très nombreux, éviter cat est très bien. Pour la plupart des questions, l'utilisation du chat est orthogonale, hors sujet, pas un problème.
Commencer ces utilisations inutiles et inutiles de la discussion de chat sur un sujet sur deux n'est que ennuyeux et ennuyeux. Obtenez une vie et attendez votre minute de gloire, lorsque vous traitez des questions de performance.
la source
file > a.sh
vaut à lui seul le temps de lire ceci :) Merci pour le partage!cat file | wc -c
,wc
doit lire stdin jusqu'à EOF, en comptant les octets. Mais dans ce cas,wc -c < file
il ne fait que stats stdin, découvre que c'est un fichier normal et affiche st_size au lieu de lire une entrée. Pour un fichier volumineux, la différence de performances serait clairement visible.Un problème supplémentaire est que le tuyau peut masquer silencieusement un sous-coque. Pour cet exemple, je vais remplacer
cat
parecho
, mais le même problème existe.Vous pourriez vous attendre
x
à contenirfoo
, mais ce n'est pas le cas. Le quex
vous avez défini était dans un sous-shell généré pour exécuter lawhile
boucle.x
dans le shell qui a démarré le pipeline a une valeur sans rapport ou n'est pas du tout définie.Dans bash4, vous pouvez configurer certaines options du shell afin que la dernière commande d'un pipeline s'exécute dans le même shell que celui qui démarre le pipeline, mais vous pouvez alors essayer ceci
et
x
est une fois de plus locale auwhile
sous-shell de.la source
shopt -s lastpipe
éviter de créer le sous-shell.En tant que personne qui le souligne régulièrement et un certain nombre d'autres anti-modèles de programmation shell, je me sens obligé de peser tardivement.
Le script Shell est un langage de copier / coller. Pour la plupart des gens qui écrivent des scripts shell, ils ne sont pas là pour apprendre la langue; c'est juste un obstacle qu'ils doivent surmonter pour continuer à faire les choses dans la ou les langues avec lesquelles ils sont réellement familiers.
Dans ce contexte, je considère qu'il est perturbateur et même potentiellement destructeur de propager divers anti-modèles de script shell. Le code que quelqu'un trouve sur Stack Overflow devrait idéalement pouvoir être copié / collé dans son environnement avec des modifications minimes et une compréhension incomplète.
Parmi les nombreuses ressources de script shell sur le net, Stack Overflow est inhabituel en ce que les utilisateurs peuvent aider à façonner la qualité du site en éditant les questions et réponses sur le site. cependant, modifications de code peuvent être problématiques car il est facile d'apporter des modifications qui n'étaient pas prévues par l'auteur du code. Par conséquent, nous avons tendance à laisser des commentaires pour suggérer des modifications du code.
L'UUCA et les commentaires antipattern associés ne sont pas réservés uniquement aux auteurs du code que nous commentons; ils sont autant une mise en garde pour aider les lecteurs du site à prendre conscience des problèmes dans le code qu'ils trouvent ici.
Nous ne pouvons pas espérer parvenir à une situation où aucune réponse sur Stack Overflow ne recommande inutile
cat
s (ou des variables non citées, ouchmod 777
, ou une grande variété d'autres fléaux antipattern), mais nous pouvons au moins aider à éduquer l'utilisateur qui est sur le point de copier / collez ce code dans la boucle étroite la plus interne de leur script qui s'exécute des millions de fois.En ce qui concerne les raisons techniques, la sagesse traditionnelle est que nous devrions essayer de minimiser le nombre de processus externes; cela continue à être une bonne indication générale lors de l'écriture de scripts shell.
la source
cat
est un grand nombre de commutateurs de contexte et de bande passante mémoire supplémentaires (et la pollution du cache L3 à partir de copies supplémentaires de données danscat
le tampon de lecture de et les tampons de canal). Surtout sur une grosse machine multicœur (comme de nombreuses configurations d'hébergement), la bande passante cache / mémoire est une ressource partagée.bzip2
et lagzip
compression sont toutes deux très lentes par rapport à la quantité de frais généraux quicat
s'ajoute à cela seul (avec la machine autrement inactive). Il est difficile de lire vos tableaux (retour à la ligne au milieu d'un nombre?).sys
le temps augmente beaucoup, mais reste petit par rapport à l'utilisateur ou réel?J'utilise souvent
cat file | myprogram
dans des exemples. Parfois, je suis accusé d'utilisation inutile de chat ( http://porkmail.org/era/unix/award.html ). Je ne suis pas d'accord pour les raisons suivantes:Il est facile de comprendre ce qui se passe.
Lors de la lecture d'une commande UNIX, vous attendez une commande suivie d'arguments suivie d'une redirection. Il est possible de placer la redirection n'importe où, mais elle est rarement vue - ainsi les gens auront plus de mal à lire l'exemple. Je crois
est plus facile à lire que
Si vous déplacez la redirection vers le début, vous confondez les personnes qui ne sont pas habituées à cette syntaxe:
et les exemples doivent être faciles à comprendre.
Il est facile de changer.
Si vous savez que le programme peut lire
cat
, vous pouvez normalement supposer qu'il peut lire la sortie de tout programme qui sort sur STDOUT, et ainsi vous pouvez l'adapter à vos propres besoins et obtenir des résultats prévisibles.Il souligne que le programme n'échoue pas, si STDIN n'est pas un fichier.
Il n'est pas sûr de supposer que si cela
program1 < foo
fonctionne,cat foo | program1
cela fonctionnera également. Cependant, il est prudent de supposer le contraire. Ce programme fonctionne si STDIN est un fichier, mais échoue si l'entrée est un tube, car il utilise la recherche:Coût de la performance
Il y a un coût pour faire le supplément
cat
. Pour donner une idée de combien j'ai exécuté quelques tests pour simuler la ligne de base (cat
), le débit faible (bzip2
), le débit moyen (gzip
) et le débit élevé (grep
).Les tests ont été exécutés sur un système bas de gamme (0,6 GHz) et un ordinateur portable ordinaire (2,2 GHz). Ils ont été exécutés 10 fois sur chaque système et le meilleur timing a été choisi pour imiter la situation optimale pour chaque test. Le $ ISO était ubuntu-11.04-desktop-i386.iso. (De jolis tableaux ici: http://oletange.blogspot.com/2013/10/useless-use-of-cat.html )
Les résultats montrent que pour un débit faible et moyen, le coût est de l'ordre de 1%. Ceci est bien dans l'incertitude des mesures, donc en pratique il n'y a pas de différence.
Pour un débit élevé, la différence est plus grande et il y a une nette différence entre les deux.
Cela mène à la conclusion: vous devriez utiliser
<
au lieu decat |
if:Sinon, peu importe que vous utilisiez
<
oucat |
.Et donc, vous ne devriez donner un prix UUoC que si et seulement si:
la source
Je pense que (la manière traditionnelle) d'utiliser le tuyau est un peu plus rapide; sur ma boîte, j'ai utilisé la
strace
commande pour voir ce qui se passe:Sans tuyau:
Et avec tuyau:
Vous pouvez faire des tests avec
strace
ettime
commander avec des commandes de plus en plus longues pour un bon benchmarking.la source
strace
montre que c'est plus rapide - lestrace
ne trace pas l'wc -l
exécution dans le second cas. Il ne trace ici que la première commande du pipeline.strace -f sh -c 'wc -l < wrong_output.c'
côtéstrace -f sh -c 'cat wrong_output.c | wc -l'
.cat
: ideone.com/2w1W42#stderrmkfifo
crée un tube nommé . Un canal anonyme est mis en place avecpipe(2)
puis forking, et le parent et l'enfant ferment différentes extrémités du tuyau. Mais oui, cette réponse est un non-sens total, et n'a même pas essayé de compter les appels système ou de l'utiliserstrace -O
pour mesurer la surcharge, ou-r
pour horodater chaque appel par rapport au dernier ...