Beaucoup de gens utilisent des liners et des scripts contenant du code le long des lignes
cat "$MYFILE" | command1 | command2 > "$OUTPUT"
Le premier cat
est souvent appelé "utilisation inutile de cat" car il nécessite techniquement de démarrer (souvent /usr/bin/cat
) un nouveau processus où cela pourrait être évité si la commande avait été exécutée.
< "$MYFILE" command1 | command2 > "$OUTPUT"
car alors le shell n'a besoin que de démarrer command1
et de simplement pointer son stdin
vers le fichier donné.
Pourquoi le shell ne fait-il pas cette conversion automatiquement? Je pense que la syntaxe "utilisation inutile de chat" est plus facile à lire et que le shell devrait avoir suffisamment d'informations pour se débarrasser automatiquement de chat inutile. Le cat
est défini dans la norme POSIX donc le shell doit être autorisé à l'implémenter en interne au lieu d'utiliser un chemin binaire dans. Le shell peut même contenir une implémentation uniquement pour une seule version d'argument et se replier sur le chemin binaire.
la source
lseek
est toujours défini le comportement et pourrait causer un résultat différent, le comportement différent de blocage peut être sémantiquement significative, etc. Il serait permis de faire le changement si vous saviez ce que les autres commandes étaient et savaient qu'ils ne se soucient pas, ou si vous ne vous souciez pas de la compatibilité à ce niveau, mais l'avantage est assez faible. J'imagine que le manque d'avantages conduit la situation plus que le coût de conformité.cat
lui-même, ou à tout autre utilitaire. Il est également permis de savoir comment fonctionnent les autres utilitaires appartenant au système (par exemple, il peut savoir comment se comporte l'grep
implémentation externe fournie avec le système ). C'est tout à fait viable à faire, il est donc tout à fait juste de se demander pourquoi ils ne le font pas.grep
. Etsed
. Etawk
. Etdu
. Et combien de centaines sinon de milliers d'autres utilitaires?Réponses:
Les 2 commandes ne sont pas équivalentes: considérez la gestion des erreurs:
cat <file that doesn't exist> | less
produira un flux vide qui sera transmis au programme canalisé ... en tant que tel, vous vous retrouvez avec un affichage ne montrant rien.< <file that doesn't exist> less
échouera à ouvrir la barre, puis ne l'ouvrira pas moins du tout.Tenter de remplacer le premier par le second pourrait casser un nombre quelconque de scripts qui s'attendent à exécuter le programme avec une entrée potentiellement vierge.
la source
cat
exécutera toujours la deuxième commande dans le pipeline tandis que la variante avec juste la redirection d'entrée n'exécutera pas la commande du tout si le fichier d'entrée est manquant.<"missing-file" grep foo | echo 2
ne s'exécutera pasgrep
mais s'exécuteraecho
.«L'utilisation inutile de
cat
» concerne davantage la façon dont vous écrivez votre code que ce qui s'exécute réellement lorsque vous exécutez le script. C'est une sorte d' anti-modèle de conception , une façon de faire quelque chose qui pourrait probablement être fait de manière plus efficace. C'est un échec dans la compréhension de la meilleure façon de combiner les outils donnés pour créer un nouvel outil. Je dirais que l'enchaînement de plusieurs commandessed
et / ouawk
ensemble dans un pipeline pourrait parfois être considéré comme un symptôme de ce même anti-modèle.La correction d'instances d '"utilisation inutile de
cat
" dans un script consiste principalement à corriger manuellement le code source du script. Un outil tel que ShellCheck peut vous aider en soulignant les cas évidents:Obtenir le shell pour le faire automatiquement serait difficile en raison de la nature des scripts shell. La façon dont un script s'exécute dépend de l'environnement hérité de son processus parent et de l'implémentation spécifique des commandes externes disponibles.
Le shell ne sait pas forcément de quoi il
cat
s'agit. Il pourrait s'agir de n'importe quelle commande de n'importe où dans votre$PATH
, ou d'une fonction.S'il s'agissait d'une commande intégrée (qu'elle peut être dans certains shells), elle aurait la possibilité de réorganiser le pipeline car elle connaîtrait la sémantique de sa
cat
commande intégrée . Avant de faire cela, il devrait en outre faire des hypothèses sur la prochaine commande dans le pipeline, après l'originalcat
.Notez que la lecture à partir d'une entrée standard se comporte légèrement différemment lorsqu'elle est connectée à un tuyau et lorsqu'elle est connectée à un fichier. Un canal n'est pas recherchable, donc selon ce que fait la prochaine commande dans le pipeline, il peut ou non se comporter différemment si le pipeline a été réorganisé (il peut détecter si l'entrée est recherchée et décider de faire les choses différemment si c'est le cas ou si il ne l'est pas, en tout cas il se comporterait alors différemment).
Cette question est similaire (dans un sens très général) à « Y a-t-il des compilateurs qui tentent de corriger les erreurs de syntaxe par eux-mêmes? » (Sur le site Software Engineering StackExchange), bien que cette question concerne évidemment les erreurs de syntaxe, pas les modèles de conception inutiles . L'idée de changer automatiquement le code en fonction de l'intention est cependant largement la même.
la source
cat
est, et les autres commandes du pipeline, (la règle comme si) et de se comporter en conséquence, elles ne le font tout simplement pas ici parce que c'est inutile et trop dur.cat /dev/tty
c'est celui qui serait différent avec<
.cat
qu'étant donné l'entrée de l'OP, il est impossible de dire ce que la commande fait réellement sans l'exécuter . Pour tout ce que vous (et le shell) savez, l'OP a une commandecat
sur son chemin qui est une simulation interactive de chat, "monfichier" est juste l'état du jeu stocké etcommand1
etcommand2
post-traite quelques statistiques sur la session de jeu en cours ...Parce que ce n'est pas inutile.
Dans le cas de
cat file | cmd
, le fd0
(stdin) decmd
sera un tube, et dans le cas decmd <file
celui-ci peut être un fichier, un périphérique, etc.Un canal a une sémantique différente d'un fichier normal, et sa sémantique n'est pas un sous-ensemble de celles d'un fichier normal:
un fichier normal ne peut pas être
select(2)
édité oupoll(2)
édité de manière significative; unselect(2)
sur il reviendra toujours "prêt". Les interfaces avancées commeepoll(2)
sous Linux ne fonctionneront tout simplement pas avec des fichiers normaux.sur Linux il y a des appels système (
splice(2)
,vmsplice(2)
,tee(2)
) qui ne fonctionne que sur les tuyaux [1]Puisqu'il
cat
est tellement utilisé, il pourrait être implémenté comme un shell intégré qui évitera un processus supplémentaire, mais une fois que vous avez commencé sur ce chemin, la même chose pourrait être faite avec la plupart des commandes - transformer le shell en un plus lent et plus maladroitperl
oupython
. il est probablement préférable d'écrire un autre langage de script avec une syntaxe de type pipe facile à utiliser pour les suites ;-)[1] Si vous voulez un exemple simple pas compensé l'occasion, vous pouvez regarder mon « binaire exec de stdin » git essentiel avec quelques explications dans le commentaire ici . L'implémenter à l'
cat
intérieur pour le faire fonctionner sans UUoC l'aurait rendu 2 ou 3 fois plus gros.la source
cat
internes.cat /dev/urandom | cpu_bound_program
exécute lesread()
appels système dans un processus distinct. Sur Linux par exemple, le travail réel du CPU de générer plus de nombres aléatoires (lorsque le pool est vide) est effectué dans cet appel système, donc l'utilisation d'un processus séparé vous permet de profiter d'un noyau CPU séparé pour générer des données aléatoires en entrée. Par exemple, dans Quel est le moyen le plus rapide de générer un fichier texte de 1 Go contenant des chiffres aléatoires?lseek
cela ne fonctionnera pas.cat foo.mp4 | mpv -
fonctionnera, mais vous ne pouvez pas chercher en arrière plus loin que le tampon de cache de mpv ou mplayer. Mais avec une entrée redirigée à partir d'un fichier, vous le pouvez.cat | mpv -
est un moyen de vérifier si un MP4 a sonmoov
atome au début du fichier, il peut donc être lu sans chercher à la fin et en arrière (c'est-à-dire s'il convient à la diffusion en continu). Il est facile d'imaginer d'autres cas où vous souhaitez tester un programme pour les fichiers non rechercheables en l'exécutant/dev/stdin
aveccat
vs par redirection.xargs cat | somecmd
. Si les chemins d'accès aux fichiers s'étendent au-delà de la limite de la mémoire tampon de commande,xargs
peuvent s'exécutercat
plusieurs fois, ce qui entraîne un flux continu, tandis que l'utilisationxargs somecmd
échoue souvent carsomecmd
il ne peut pas être exécuté en plusieurs pour obtenir un résultat homogène.Parce que détecter un chat inutile est vraiment très difficile.
J'avais un script shell où j'écrivais
Le script shell a échoué en production si le a
cat
été supprimé car il a été appelé viasu -c 'script.sh' someuser
. L'apparence superflue acat
amené le propriétaire de l'entrée standard à changer l'utilisateur que le script exécutait, de sorte que sa réouverture via a/proc
fonctionné.la source
cat
suivi par exactement un paramètre, de sorte que le shell devrait utiliser un véritablecat
exécutable au lieu d'un raccourci optimisé. Bon point sur les informations d'identification éventuellement différentes ou stdin non standard pour les processus réels, cependant.tl; dr: les shells ne le font pas automatiquement car les coûts dépassent les bénéfices probables.
D'autres réponses ont souligné la différence technique entre stdin étant un tuyau et un fichier. Gardant cela à l'esprit, le shell pourrait faire l'une des choses suivantes:
cat
tant que intégré, tout en préservant la distinction fichier / canal. Cela permettrait d'économiser le coût d'un exec et peut-être, éventuellement, d'une fourchette.Ensuite, vous devez considérer les coûts et les avantages de chaque approche. Les avantages sont assez simples:
cat
)Vous économisez donc un peu de temps CPU et de mémoire, surtout si vous pouvez éviter le fork. Bien sûr, vous économisez ce temps et cette mémoire uniquement lorsque la fonctionnalité est réellement utilisée. Et vous ne faites vraiment que gagner du temps fork / exec; avec des fichiers plus volumineux, le temps est principalement le temps d'E / S (c'est-à-dire que cat lit un fichier à partir du disque). Vous devez donc vous demander: à quelle fréquence est
cat
utilisée (inutilement) dans les scripts shell où les performances comptent réellement? Comparez-le à d'autres structures de shell courantes commetest
- il est difficile d'imaginer qu'ilcat
est utilisé (inutilement) même un dixième aussi souvent quetest
dans des endroits importants. C'est une supposition, je n'ai pas mesuré, ce que vous voudriez faire avant toute tentative de mise en œuvre. (Ou de la même manière, demander à quelqu'un d'autre de l'implémenter dans, par exemple, une demande de fonctionnalité.)Ensuite, vous demandez: quels sont les coûts. Les deux coûts qui viennent à l'esprit sont (a) du code supplémentaire dans le shell, ce qui augmente sa taille (et donc éventuellement l'utilisation de la mémoire), nécessite plus de travail de maintenance, est un autre endroit pour les bugs, etc .; et (b) les surprises de compatibilité ascendante, POSIX
cat
omet beaucoup de fonctionnalités, par exemple, GNU coreutilscat
, vous devez donc faire attention exactement à ce que le programmecat
intégré implémenterait.L'option intégrée supplémentaire n'est probablement pas si mauvaise - en ajoutant une autre intégrée là où un groupe existe déjà. Si vous aviez des données de profilage montrant que cela aiderait, vous pourriez probablement convaincre les auteurs de votre shell préféré de les ajouter.
En ce qui concerne l'analyse du pipeline, je ne pense pas que les shells font quelque chose comme ça actuellement (quelques-uns reconnaissent la fin d'un pipeline et peuvent éviter un fork). Essentiellement, vous ajouteriez un optimiseur (primitif) au shell; les optimiseurs s'avèrent souvent être du code compliqué et la source de nombreux bugs. Et ces bogues peuvent être surprenants - de légers changements dans le script shell pourraient finir par éviter ou déclencher le bogue.
Postscript: vous pouvez appliquer une analyse similaire à vos utilisations inutiles du chat. Avantages: plus facile à lire (bien que si command1 prendra un fichier comme argument, probablement pas). Coûts: fork et exec supplémentaires (et si command1 peut prendre un fichier en argument, probablement des messages d'erreur plus confus). Si votre analyse vous dit d'utiliser inutilement cat, alors allez-y.
la source
La
cat
commande peut accepter-
comme marqueur pour stdin . ( POSIX , " Si un fichier est '-', l'utilitaire cat doit lire à partir de l'entrée standard à ce point de la séquence. ") Cela permet une gestion simple d'un fichier ou d'un stdin où sinon cela serait interdit.Considérez ces deux alternatives triviales, où l'argument shell
$1
est-
:Un autre moment
cat
est utile: il est utilisé intentionnellement comme no-op simplement pour maintenir la syntaxe du shell:Enfin, je crois que le seul moment où UUOC peut vraiment être correctement appelé est lorsqu'il
cat
est utilisé avec un nom de fichier connu pour être un fichier normal (c'est-à-dire pas un périphérique ou un canal nommé), et qu'aucun indicateur n'est donné à la commande:Dans toute autre situation, les propriétés propres
cat
peuvent être requises.la source
La commande cat peut faire des choses que le shell ne peut pas nécessairement faire (ou du moins, ne peut pas faire facilement). Par exemple, supposons que vous souhaitiez imprimer des caractères qui pourraient autrement être invisibles, tels que des tabulations, des retours chariot ou des retours à la ligne. Il y a * peut-être * un moyen de le faire avec seulement des commandes intégrées au shell, mais je ne peux en penser à aucune du haut de ma tête. La version GNU de cat peut le faire avec l'
-A
argument ou les-v -E -T
arguments (cependant je ne connais pas les autres versions de cat). Vous pouvez également préfixer chaque ligne avec un numéro de ligne en utilisant-n
(encore une fois, IDK si les versions non GNU peuvent le faire).Un autre avantage de cat est qu'il peut facilement lire plusieurs fichiers. Pour ce faire, il suffit de taper
cat file1 file2 file3
. Pour faire de même avec un shell, les choses deviendraient délicates, bien qu'une boucle soigneusement conçue pourrait très probablement obtenir le même résultat. Cela dit, voulez-vous vraiment prendre le temps d'écrire une telle boucle, alors qu'il existe une alternative aussi simple? Je ne!La lecture de fichiers avec cat utiliserait probablement moins de CPU que le shell, car cat est un programme précompilé (l'exception évidente est tout shell qui a un chat intégré). Lors de la lecture d'un grand groupe de fichiers, cela peut devenir apparent, mais je ne l'ai jamais fait sur mes machines, donc je ne peux pas en être sûr.
La commande cat peut également être utile pour forcer une commande à accepter une entrée standard dans des cas où elle ne le pourrait pas. Considérer ce qui suit:
echo 8 | sleep
Le nombre "8" ne sera pas accepté par la commande "sleep", car il n'a jamais vraiment été conçu pour accepter une entrée standard. Ainsi, le sommeil ignorera cette entrée, se plaindra d'un manque d'arguments et quittera. Cependant, si l'on tape:
echo 8 | sleep $(cat)
De nombreux obus vont s'étendre à
sleep 8
, et le sommeil attendra 8 secondes avant de sortir. Vous pouvez également faire quelque chose de similaire avec ssh:command | ssh 1.2.3.4 'cat >> example-file'
Cette commande avec append exemple-fichier sur la machine avec l'adresse 1.2.3.4 avec tout ce qui est sorti de "commande".
Et c'est (probablement) juste gratter la surface. Je suis sûr que je pourrais trouver plus d'exemples de chats utiles si je le voulais, mais ce post est assez long. Donc, je conclurai en disant ceci: demander au shell d'anticiper tous ces scénarios (et plusieurs autres) n'est pas vraiment faisable.
la source
N'oubliez pas qu'un utilisateur peut avoir un
cat
dans son$PATH
qui n'est pas exactement le POSIXcat
(mais peut-être une variante qui pourrait enregistrer quelque chose quelque part). Dans ce cas, vous ne voulez pas que le shell le supprime.Le
PATH
pourrait changer de façon dynamique, etcat
n'est pas ce que vous croyez qu'il est. Il serait assez difficile d'écrire un shell faisant l'optimisation dont vous rêvez.De plus, dans la pratique,
cat
c'est un programme assez rapide. Il y a peu de raisons pratiques (sauf esthétiques) pour l'éviter.Voir également l'excellent discours d' enfer Parsing POSIX [s] de Yann Regis-Gianas au FOSDEM2018. Cela donne d'autres bonnes raisons d'éviter d'essayer de faire ce dont vous rêvez dans une coquille.
Si les performances étaient vraiment un problème pour les shells, quelqu'un aurait proposé un shell qui utilise une optimisation sophistiquée du compilateur de programme entier, une analyse de code source statique et des techniques de compilation juste à temps (ces trois domaines ont des décennies de progrès et des publications scientifiques et dédiés conférences, par exemple dans le cadre de SIGPLAN ). Malheureusement, même en tant que sujet de recherche intéressant, qui n'est actuellement pas financé par des agences de recherche ou des investisseurs en capital-risque, et j'en déduis que cela n'en vaut tout simplement pas la peine. En d'autres termes, il n'y a probablement pas de marché significatif pour l'optimisation des coques . Si vous avez un demi-million d'euros à dépenser pour de telles recherches, vous trouverez facilement quelqu'un pour le faire, et je pense que cela donnerait des résultats intéressants.
D'un point de vue pratique, la réécriture, pour améliorer ses performances, un petit script shell (une centaine de lignes) dans n'importe quel meilleur langage de script (Python, AWK, Guile, ...) est couramment réalisé. Et il n'est pas raisonnable (pour de nombreuses raisons d'ingénierie logicielle) d'écrire de gros scripts shell: lorsque vous écrivez un script shell dépassant une centaine de lignes, vous devez envisager de le réécrire (même pour des raisons de lisibilité et de maintenance) dans un langage plus approprié. : en tant que langage de programmation, le shell est très pauvre. Cependant, il existe de nombreux scripts shell générés , et pour de bonnes raisons (par exemple, les
configure
scripts générés par autoconf GNU ).En ce qui concerne les fichiers textuels volumineux, les passer en
cat
tant qu'argument unique n'est pas une bonne pratique, et la plupart des administrateurs système le savent (lorsque l'exécution d'un script shell prend plus d'une minute, vous commencez à envisager de l'optimiser). Pour les gros fichiers gigaoctets, cecat
n'est jamais le bon outil pour les traiter.la source
cat some-huge-log | tail -n 5
courir (oùtail -n 5 some-huge-log
pourrait sauter directement à la fin, alors qu'ellecat
ne lit que de l'avant vers l'arrière) serait en désaccord.cat
un fichier texte volumineux dans des dizaines de Go (qui a été créé pour les tests) prend un peu de temps. Je ne le recommanderais pas.En ajoutant à la réponse @Kusalananda (et au commentaire @alephzero), le chat pourrait être n'importe quoi:
ou
Il n'y a aucune raison que cat (seul) ou / usr / bin / cat sur le système soit en fait cat l'outil de concaténation.
la source
cat
est défini par POSIX et ne devrait donc pas être très différent.PATH=/home/Joshua/bin:$PATH cat ...
Êtes-vous sûr de savoir ce qui secat
passe maintenant?cat
deux qu'il est possible de passer outre, mais nous savons tous les deux qu'il ne doit pas être remplacé sans motif par quelque chose d'autre. Mon commentaire souligne que POSIX impose un (sous-ensemble de) comportement particulier dont on peut raisonnablement s'attendre à ce qu'il existe. J'ai parfois écrit un script shell qui étend le comportement d'un utilitaire standard. Dans ce cas, le script shell a agi et s'est comporté exactement comme l'outil qu'il a remplacé, sauf qu'il avait des capacités supplémentaires./bin/cat
. (Et vous en feriez une option que vous pourriez désactiver.) Ou vous feriezcat
un shell intégré (qui peut peut-être revenir à/bin/cat
plusieurs arguments?) Afin que les utilisateurs puissent contrôler s'ils voulaient ou non que la version externe soit normale. façon, avecenable cat
. Comme pourkill
. (Je pensais que bashcommand cat
fonctionnerait, mais cela ne saute pas les buildins)cat
dans cet environnement ne fait plus référence à l'habituelcat
. De toute évidence, l'optimisation doit être implémentée après le traitement des alias. Je considère que les commandes intégrées au shell représentent des commandes dans un répertoire virtuel qui est toujours ajouté à votre chemin. Si vous voulez éviter la version intégrée du shell d'une commande (par exempletest
), vous devez utiliser une variante avec un chemin.Deux utilisations "inutiles" du chat:
... ici
cat
est utilisé pour mélanger le fichier et l'entrée canalisée.... ici
xargs
peut accepter un nombre pratiquement infini de noms de fichiers et s'exécutercat
autant de fois que nécessaire, tout en se comportant comme un seul flux. Cela fonctionne donc pour les grandes listes de fichiers où l'utilisation directe dexargs sort
ne le fait pas.la source
cat
est appelé avec exactement un argument. Surtout dans le cas oùsh
une chaîne est passée etxargs
appellecat
directement, il n'y a aucun moyen pour le shell d'utiliser son implémentation intégrée.Mis à part d'autres choses,
cat
-check ajouterait une surcharge de performances supplémentaire et une confusion quant à l'utilisation quicat
est réellement inutile , à mon humble avis , car ces vérifications peuvent être inefficaces et créer des problèmes avec unecat
utilisation légitime .Lorsque les commandes traitent des flux standard, elles n'ont qu'à se soucier de la lecture / écriture dans les descripteurs de fichiers standard. Les commandes peuvent savoir si stdin peut être recherché / recherché ou non, ce qui indique un canal ou un fichier.
Si nous ajoutons au mélange en vérifiant quel processus fournit réellement ce contenu stdin, nous devrons trouver le processus de l'autre côté du tuyau et appliquer l'optimisation appropriée. Cela peut être fait en termes de shell lui-même, comme le montre le post SuperUser de Kyle Jones, et en termes de shell qui est
comme indiqué dans le post lié. Il s'agit de 3 commandes supplémentaires (donc des
fork()
s etexec()
s supplémentaires ) et des traversées récursives (donc beaucoup d'readdir()
appels).En termes de C et de code source du shell, le shell connaît déjà le processus enfant, il n'y a donc pas besoin de récursivité, mais comment savoir quand optimiser et quand
cat
est-il réellement inutile? Il existe en fait des utilisations utiles du chat , commeIl serait probablement inutile et inutile d'ajouter une telle optimisation au shell. Comme la réponse de Kusalanda l'a déjà mentionné, UUOC concerne davantage le manque de compréhension de l'utilisateur quant à la meilleure façon de combiner les commandes pour de meilleurs résultats.
la source