Il y a plusieurs choses à considérer ici.
i=`cat input`
peut être cher et il y a beaucoup de variations entre les coques.
C'est une fonctionnalité appelée substitution de commandes. L'idée est de stocker la sortie entière de la commande moins les caractères de fin de ligne dans la ivariable en mémoire.
Pour ce faire, les shells forkent la commande dans un sous-shell et lisent sa sortie via un tube ou une paire de sockets. Vous voyez beaucoup de variations ici. Sur un fichier de 50 Mo ici, je peux voir par exemple que bash est 6 fois plus lent que ksh93 mais légèrement plus rapide que zsh et deux fois plus rapide que yash.
La principale raison d' bashêtre lent est qu'il lit à partir du canal 128 octets à la fois (tandis que d'autres shells lisent 4KiB ou 8KiB à la fois) et est pénalisé par la surcharge des appels système.
zshdoit effectuer un post-traitement pour échapper aux octets NUL (d'autres shells se brisent sur les octets NUL), et yasheffectue un traitement encore plus intensif en analysant les caractères multi-octets.
Tous les shells doivent supprimer les caractères de fin de ligne qu'ils peuvent faire plus ou moins efficacement.
Certains peuvent vouloir gérer les octets NUL plus gracieusement que d'autres et vérifier leur présence.
Ensuite, une fois que vous avez cette grande variable en mémoire, toute manipulation implique généralement d'allouer plus de mémoire et de copier les données.
Ici, vous passez (vouliez passer) le contenu de la variable à echo.
Heureusement, echoest intégré dans votre shell, sinon l'exécution aurait probablement échoué avec une erreur de liste d'arguments trop longue . Même dans ce cas, la construction du tableau de la liste des arguments impliquera éventuellement la copie du contenu de la variable.
L'autre problème principal dans votre approche de substitution de commandes est que vous invoquez l' opérateur split + glob (en oubliant de citer la variable).
Pour cela, les shells doivent traiter la chaîne comme une chaîne de caractères (bien que certains shells ne le soient pas et soient bogués à cet égard), donc dans les environnements locaux UTF-8, cela signifie analyser les séquences UTF-8 (si ce n'est déjà fait comme le yashfait le cas) , recherchez les $IFScaractères dans la chaîne. S'il $IFScontient de l'espace, une tabulation ou une nouvelle ligne (ce qui est le cas par défaut), l'algorithme est encore plus complexe et coûteux. Ensuite, les mots résultant de ce fractionnement doivent être alloués et copiés.
La partie glob sera encore plus chère. Si l' un de ces mots contiennent des caractères glob ( *, ?, [), le shell devra lire le contenu de certains répertoires et faire un peu de correspondance de motif coûteux ( bashla mise en œuvre « , par exemple , est notoirement très mal à cela).
Si l'entrée contient quelque chose comme /*/*/*/../../../*/*/*/../../../*/*/*ça, cela coûtera extrêmement cher car cela signifie répertorier des milliers d'annuaires et cela peut s'étendre à plusieurs centaines de Mio.
Ensuite, echoil effectuera généralement un traitement supplémentaire. Certaines implémentations développent des \xséquences dans l'argument qu'il reçoit, ce qui signifie analyser le contenu et probablement une autre allocation et copie des données.
D'un autre côté, OK, dans la plupart des shells catn'est pas intégré, ce qui signifie bifurquer un processus et l'exécuter (donc charger le code et les bibliothèques), mais après la première invocation, ce code et le contenu du fichier d'entrée sera mis en cache en mémoire. En revanche, il n'y aura pas d'intermédiaire. catlira de grandes quantités à la fois et l'écrira immédiatement sans traitement, et il n'a pas besoin d'allouer une énorme quantité de mémoire, juste ce tampon qu'il réutilise.
Cela signifie également qu'il est beaucoup plus fiable car il ne s'étouffe pas sur les octets NUL et ne supprime pas les caractères de fin de ligne (et ne fait pas de split + glob, bien que vous puissiez éviter cela en citant la variable et ne pas développez la séquence d'échappement bien que vous puissiez éviter cela en utilisant printfau lieu de echo).
Si vous souhaitez l'optimiser davantage, au lieu d'appeler catplusieurs fois, passez simplement inputplusieurs fois à cat.
yes input | head -n 100 | xargs cat
Exécute 3 commandes au lieu de 100.
Pour rendre la version variable plus fiable, vous devez utiliser zsh(les autres shells ne peuvent pas gérer les octets NUL) et le faire:
zmodload zsh/mapfile
var=$mapfile[input]
repeat 10 print -rn -- "$var"
Si vous savez que l'entrée ne contient pas d'octets NUL, vous pouvez le faire de manière fiable POSIXly (bien que cela puisse ne pas fonctionner là où il printfn'est pas intégré) avec:
i=$(cat input && echo .) || exit # add an extra .\n to avoid trimming newlines
i=${i%.} # remove that trailing dot (the \n was removed by cmdsubst)
n=10
while [ "$n" -gt 10 ]; do
printf %s "$i"
n=$((n - 1))
done
Mais cela ne sera jamais plus efficace que l'utilisation catdans la boucle (sauf si l'entrée est très petite).
cat $(for i in $(seq 1 10); do echo "input"; done) >> output? :)