Pourquoi le shell ne corrige-t-il pas automatiquement «l'utilisation inutile du chat»? [fermé]

28

Beaucoup de gens utilisent des liners et des scripts contenant du code le long des lignes

cat "$MYFILE" | command1 | command2 > "$OUTPUT"

Le premier catest 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 command1et de simplement pointer son stdinvers 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 catest 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.

Mikko Rantalainen
la source
22
Ces commandes ne sont pas réellement équivalentes, car dans un cas, stdin est un fichier et dans l'autre, c'est un canal, donc ce ne serait pas une conversion strictement sûre. Vous pourriez cependant créer un système qui le ferait.
Michael Homer
14
Le fait que vous ne puissiez pas imaginer un cas d'utilisation ne signifie pas qu'une application n'est pas autorisée à s'appuyer inutilement sur le comportement spécifié. Obtenir une erreur de lseekest 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é.
Michael Homer
3
Le shell est absolument autorisé à s'implémenter catlui-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' grepimplé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.
Michael Homer
6
@MichaelHomer, par exemple, il peut savoir comment se comporte l' implémentation de grep externe fournie avec le système. Ainsi, le shell dépend maintenant du comportement de grep. Et sed. Et awk. Et du. Et combien de centaines sinon de milliers d'autres utilitaires?
Andrew Henle
19
Il serait assez peu cool de ma part de modifier mes commandes pour moi.
Azor Ahai

Réponses:

25

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.

UKMonkey
la source
1
Je marquerai votre réponse comme acceptée car je pense que c'est la différence la plus importante entre les deux syntaxes. La variante avec catexé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.
Mikko Rantalainen
Cependant, notez que <"missing-file" grep foo | echo 2ne s'exécutera pas grepmais s'exécutera echo.
Mikko Rantalainen
51

«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 commandes sedet / ou awkensemble 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:

$ cat script.sh
#!/bin/sh
cat file | cat
$ shellcheck script.sh

In script.sh line 2:
cat file | cat
    ^-- SC2002: Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead.

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 cats'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 catcommande 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'original cat.

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.

Kusalananda
la source
Il est parfaitement conforme pour un shell de savoir ce qui catest, 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.
Michael Homer
4
@MichaelHomer Oui. Mais il est également autorisé de surcharger une commande standard avec une fonction du même nom.
Kusalananda
2
@PhilipCouling Il est absolument conforme tant qu'il est connu qu'aucune des commandes du pipeline ne s'en soucie. Le shell est spécifiquement autorisé à remplacer les utilitaires par des fonctions intégrées ou shell et ceux-ci n'ont aucune restriction d'environnement d'exécution, aussi longtemps que le résultat externe est indiscernable, il est autorisé. Pour votre cas, cat /dev/ttyc'est celui qui serait différent avec <.
Michael Homer
1
@MichaelHomer donc tant que le résultat externe est indiscernable, il est autorisé Cela signifie que le comportement de l'ensemble des utilitaires optimisés de cette manière ne peut jamais changer . Cela doit être l'enfer ultime de la dépendance.
Andrew Henle
3
@MichaelHomer Comme les autres commentaires l'ont dit, bien sûr, il est parfaitement conforme pour le shell de savoircat 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 commande catsur son chemin qui est une simulation interactive de chat, "monfichier" est juste l'état du jeu stocké et command1et command2post-traite quelques statistiques sur la session de jeu en cours ...
alephzero
34

Parce que ce n'est pas inutile.

Dans le cas de cat file | cmd, le fd 0(stdin) de cmdsera un tube, et dans le cas de cmd <filecelui-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é ou poll(2)édité de manière significative; un select(2)sur il reviendra toujours "prêt". Les interfaces avancées comme epoll(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 catest 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 maladroit perlou python. 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' catintérieur pour le faire fonctionner sans UUoC l'aurait rendu 2 ou 3 fois plus gros.

mosvy
la source
2
En fait, ksh93 - t mettre en œuvre certaines commandes externes comme catinternes.
jrw32982 prend en charge Monica
3
cat /dev/urandom | cpu_bound_programexécute les read()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?
Peter Cordes
4
Plus important encore dans la plupart des cas, cela signifie que lseekcela 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 son moovatome 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/stdinavec catvs par redirection.
Peter Cordes
Cela est encore plus vrai lors de l'utilisation 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, xargspeuvent s'exécuter catplusieurs fois, ce qui entraîne un flux continu, tandis que l'utilisation xargs somecmdéchoue souvent car somecmdil ne peut pas être exécuté en plusieurs pour obtenir un résultat homogène.
tasket
17

Parce que détecter un chat inutile est vraiment très difficile.

J'avais un script shell où j'écrivais

cat | (somecommand <<!
...
/proc/self/fd/3
...
!) 0<&3

Le script shell a échoué en production si le a catété supprimé car il a été appelé via su -c 'script.sh' someuser. L'apparence superflue a catamené le propriétaire de l'entrée standard à changer l'utilisateur que le script exécutait, de sorte que sa réouverture via a /procfonctionné.

Joshua
la source
Ce cas serait assez facile car il ne suit clairement pas le modèle simple de catsuivi par exactement un paramètre, de sorte que le shell devrait utiliser un véritable catexé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.
Mikko Rantalainen
13

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:

  1. Implémentez en cattant 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.
  2. Effectuez une analyse complète du pipeline en connaissant les différentes commandes utilisées pour voir si le fichier / pipe est important, puis agissez en fonction de cela.

Ensuite, vous devez considérer les coûts et les avantages de chaque approche. Les avantages sont assez simples:

  1. Dans les deux cas, évitez un exec (de cat)
  2. Dans le second cas, lorsque la substitution de redirection est possible, évite un fork.
  3. Dans les cas où vous devez utiliser un tuyau, il peut parfois être possible d'éviter une fourche / vfork, mais souvent pas. En effet, l'équivalent cat doit s'exécuter en même temps que le reste du pipeline.

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 catutilisée (inutilement) dans les scripts shell où les performances comptent réellement? Comparez-le à d'autres structures de shell courantes comme test- il est difficile d'imaginer qu'il catest utilisé (inutilement) même un dixième aussi souvent que testdans 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 catomet beaucoup de fonctionnalités, par exemple, GNU coreutils cat, vous devez donc faire attention exactement à ce que le programme catintégré implémenterait.

  1. 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.

  2. 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.

derobert
la source
10

La catcommande 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 $1est -:

cat "$1" | nl    # Works completely transparently
nl < "$1"        # Fails with 'bash: -: No such file or directory'

Un autre moment catest utile: il est utilisé intentionnellement comme no-op simplement pour maintenir la syntaxe du shell:

file="$1"
reader=cat
[[ $file =~ \.gz$ ]] && reader=zcat
[[ $file =~ \.bz2$ ]] && reader=bzcat
"$reader" "$file"

Enfin, je crois que le seul moment où UUOC peut vraiment être correctement appelé est lorsqu'il catest 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:

cat file.txt

Dans toute autre situation, les propriétés propres catpeuvent être requises.

roaima
la source
6

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' -Aargument ou les -v -E -Targuments (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.

TSJNachos117
la source
Je terminerais la dernière phrase par "n'est pas facilement réalisable"
Basile Starynkevitch
3

N'oubliez pas qu'un utilisateur peut avoir un catdans son $PATHqui n'est pas exactement le POSIX cat(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, et cat 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 configurescripts générés par autoconf GNU ).

En ce qui concerne les fichiers textuels volumineux, les passer en cattant 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, ce catn'est jamais le bon outil pour les traiter.

Basile Starynkevitch
la source
3
"Assez peu de raisons pratiques pour l'éviter" - toute personne qui a attendu pour cat some-huge-log | tail -n 5courir (où tail -n 5 some-huge-logpourrait sauter directement à la fin, alors qu'elle catne lit que de l'avant vers l'arrière) serait en désaccord.
Charles Duffy
Le commentaire extrait catun 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.
Sergiy Kolodyazhnyy
1
BTW, re: "pas de marché significatif pour l'optimisation des shells" - ksh93 est un shell d'optimisation, et un assez bon. Il a été , pendant un certain temps, vendu avec succès comme produit commercial. (Malheureusement, le fait d'avoir une licence commerciale a également fait suffisamment de niche pour que des clones mal écrits et d'autres successeurs moins capables mais gratuits aient pris le contrôle du monde en dehors de ces sites désireux de payer pour une licence, conduisant à la situation que nous avoir aujourd'hui).
Charles Duffy
(n'utilisant pas les techniques spécifiques que vous notez, mais franchement, ces techniques n'ont pas de sens étant donné le modèle de processus; les techniques qu'il applique sont, bien, bien appliquées et à bon escient ).
Charles Duffy
2

En ajoutant à la réponse @Kusalananda (et au commentaire @alephzero), le chat pourrait être n'importe quoi:

alias cat='gcc -c'
cat "$MYFILE" | command1 | command2 > "$OUTPUT"

ou

echo 'echo 1' > /usr/bin/cat
cat "$MYFILE" | command1 | command2 > "$OUTPUT"

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.

Rob
la source
3
Autre que le comportement de catest défini par POSIX et ne devrait donc pas être très différent.
roaima
2
@roaima: PATH=/home/Joshua/bin:$PATH cat ...Êtes-vous sûr de savoir ce qui se catpasse maintenant?
Joshua
1
@Joshua ça n'a pas vraiment d'importance. Nous savons tous les catdeux 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.
roaima
@Joshua: Sur la plupart des plateformes, les shells savent (ou pourraient savoir) quels répertoires contiennent les exécutables qui implémentent les commandes POSIX. Ainsi, vous pouvez simplement reporter la substitution jusqu'à l'expansion de l'alias et la résolution du chemin, et ne le faire que pour /bin/cat. (Et vous en feriez une option que vous pourriez désactiver.) Ou vous feriez catun shell intégré (qui peut peut-être revenir à /bin/catplusieurs arguments?) Afin que les utilisateurs puissent contrôler s'ils voulaient ou non que la version externe soit normale. façon, avec enable cat. Comme pour kill. (Je pensais que bash command catfonctionnerait, mais cela ne saute pas les buildins)
Peter Cordes
Si vous fournissez un alias, le shell saura que catdans cet environnement ne fait plus référence à l'habituel cat. 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 exemple test), vous devez utiliser une variante avec un chemin.
Mikko Rantalainen
1

Deux utilisations "inutiles" du chat:

sort file.txt | cat header.txt - footer.txt | less

... ici catest utilisé pour mélanger le fichier et l'entrée canalisée.

find . -name '*.info' -type f | sh -c 'xargs cat' | sort

... ici xargspeut accepter un nombre pratiquement infini de noms de fichiers et s'exécuter catautant 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 de xargs sortne le fait pas.

tasket
la source
Ces deux cas d'utilisation seraient évités de façon triviale en ne faisant intervenir le shell intégré que s'il catest appelé avec exactement un argument. Surtout dans le cas où shune chaîne est passée et xargsappelle catdirectement, il n'y a aucun moyen pour le shell d'utiliser son implémentation intégrée.
Mikko Rantalainen
0

Mis à part d'autres choses, cat-check ajouterait une surcharge de performances supplémentaire et une confusion quant à l'utilisation qui catest réellement inutile , à mon humble avis , car ces vérifications peuvent être inefficaces et créer des problèmes avec une catutilisation 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

(find /proc -type l | xargs ls -l | fgrep 'pipe:[20043922]') 2>/dev/null

comme indiqué dans le post lié. Il s'agit de 3 commandes supplémentaires (donc des fork()s et exec()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 catest-il réellement inutile? Il existe en fait des utilisations utiles du chat , comme

# adding header and footer to file
( cmd; cat file; cmd ) | cmd
# tr command does not accept files as arguments
cat log1 log2 log3 | tr '[:upper:]' '[:lower:]'

Il 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.

Sergiy Kolodyazhnyy
la source