web-dev-qa-db-fra.com

Commande de sortie du contenu du fichier vers stdout?

Je sais que cat peut le faire, mais son objectif principal est de concaténer plutôt que de simplement afficher le contenu.

Je connais également less et more, mais je cherche quelque chose de simple (pas un pager) qui ne fait que sortir le contenu d'un fichier sur le terminal et c'est fait spécialement pour ça, s'il y a une telle chose.

30
confused00

Le plus évident est cat. Mais jetez également un œil à head et tail. Il existe également d'autres utilitaires Shell pour imprimer un fichier ligne par ligne: sed, awk, grep. Mais ceux-ci consistent à alterner le contenu du fichier ou à rechercher à l'intérieur du fichier.

J'ai fait quelques tests pour estimer lequel est le plus efficace. J'exécute tous les creux strace pour voir lequel a fait le moins d'appels système. Mon fichier contient 1275 lignes.

  • awk: 1355 appels système
  • cat: 51 appels système
  • grep: 1337 appels système
  • head: 93 appels système
  • tail: 130 appels système
  • sed: 1378 appels système

Comme vous pouvez le voir, même si cat a été conçu pour concaténer des fichiers, il est le plus rapide et le plus efficace. sed, awk et grep ont imprimé le fichier ligne par ligne, c'est pourquoi ils ont plus de 1275 appels système.

40
chaos

Je sais que cat peut le faire, mais son objectif principal est de concaténer plutôt que de simplement afficher le contenu.

Le but de cat est exactement cela, lire un fichier et le sortir vers stdout.

22
Jan

Tout d'abord, cat écrit dans la sortie standard, qui n'est pas nécessairement un terminal, même si cat a été tapé dans le cadre d'une commande dans un shell interactif. Si vous avez vraiment besoin de quelque chose à écrire sur le terminal même lorsque la sortie standard est redirigée, ce n'est pas si facile (vous devez spécifier quel terminal, et il pourrait même ne pas y en avoir un si la commande est exécutée à partir d'un script), même si un pourrait (ab) utiliser la sortie d'erreur standard si la commande fait simplement partie d'un pipeline. Mais puisque vous avez indiqué que cat fait le travail, je suppose que vous ne posiez pas de question sur une telle situation.

Si votre objectif était d'envoyer ce qui est écrit dans la sortie standard dans un pipeline, alors l'utilisation de cat serait éligible au seless Use of Cat Award , puisque cat file | pipeline (où pipeline représente n'importe quel pipeline) peut être fait plus efficacement que <file pipeline. Mais encore une fois, de votre formulation, je déduis que ce n'était pas votre intention.

Il n'est donc pas si clair de quoi vous vous inquiétez. Si vous trouvez cat trop long pour taper, vous pouvez définir un alias à un ou deux caractères (il y a encore quelques noms de ce type qui restent inutilisés dans Unix standard). Si toutefois vous craignez que cat passe des cycles inutiles, vous ne devriez pas.

S'il y avait un programme null qui ne prend aucun argument et copie simplement l'entrée standard sur la sortie standard (l'objet neutre pour les pipelines), vous pourriez faire ce que vous voulez avec <file null. Il n'y a pas un tel programme, bien qu'il soit facile à écrire (un programme C avec juste une fonction main sur une ligne peut faire le travail), mais appeler cat sans arguments (ou cat - Si vous voulez être explicite) fait exactement cela.

S'il y avait un programme nocat qui prend exactement un argument de nom de fichier, essaie d'ouvrir le fichier, se plaint s'il ne peut pas, et sinon procède à la copie du fichier vers la sortie standard, alors ce serait exactement ce que vous êtes Demander. Il n'est que légèrement plus difficile à écrire que null, le travail principal étant d'ouvrir le fichier, de tester et éventuellement de se plaindre (si vous êtes méticuleux, vous voudrez peut-être également inclure un test indiquant qu'il existe exactement un argument, et se plaindre du contraire). Mais là encore, cat, désormais fourni avec un seul argument, fait exactement cela, donc il n'y a pas besoin de programme nocat.

Une fois que vous avez réussi à écrire le programme nocat, pourquoi s'arrêter à un seul argument? Envelopper le code dans une boucle for(;*argp!=NULL;++argp) ne demande vraiment aucun effort, ajoute tout au plus quelques instructions machine au binaire, et évite d'avoir à se plaindre d'un nombre incorrect d'arguments (ce qui épargne beaucoup plus d'instructions) . Voilà une version primitive de cat, concaténant des fichiers. (Pour être honnête, vous devez le modifier un peu afin que, sans arguments, il se comporte comme null.)

Bien sûr, dans le vrai programme cat, ils ont ajouté quelques cloches et sifflets, car ils le font toujours. Mais l'essentiel est que l'aspect "concaténation" de cat ne coûte vraiment aucun effort, ni pour le programmeur ni pour la machine qui l'exécute. Le fait que cat subsume null et nocat explique la non-existence de tels programmes. Évitez d'utiliser cat avec un seul argument si le résultat va dans un pipeline, mais s'il est utilisé uniquement pour afficher le contenu du fichier sur le terminal, même la page que j'ai liée admet qu'il s'agit d'une utilisation utile de cat, alors n'hésitez pas.


Vous pouvez tester que cat est vraiment implémenté par une simple boucle autour d'une fonctionnalité nocat hypothétique, en appelant cat avec plusieurs noms de fichiers dont un nom invalide, pas dans le premier position: plutôt que de se plaindre immédiatement que ce fichier n'existe pas, cat vide d'abord les fichiers valides précédents, puis se plaint du fichier invalide (du moins c'est ainsi que se comporte mon chat).

11
Marc van Leeuwen

Sous zsh essayez

<file

Je pense que c'est le moyen le plus court d'imprimer un fichier. Il utilise 'caché' cat (ou more si stdout est un terminal), mais la commande utilisée pour l'impression est contrôlée par la variable READNULLCMD que vous pouvez écraser en toute sécurité directement par commande nom ou même par une fonction. Par exemple, pour imprimer des fichiers avec une numérotation de ligne:

numcat() { nl -s'> ' -w2 - }
READNULLCMD=numcat
<file
7
jimmij

Je sais que c'est une question du passé. Techniquement, puisque l'impression du contenu d'un fichier dans stdout est une forme de concaténation, cat est sémantiquement appropriée. N'oubliez pas que printf est sémantiquement destiné à formater et imprimer des données. Bash fournit également une syntaxe pour rediriger l'entrée et la sortie des fichiers. Une combinaison de ces éléments pourrait produire ceci:

printf '%s' "$(<file.txt)"
5
James M. Lay

POSIX définit cat comme:

[~ # ~] nom [~ # ~]

cat - concaténer et imprimer des fichiers

[~ # ~] synopsis [~ # ~]

chat [-u] [fichier ...]

[~ # ~] description [~ # ~]

L'utilitaire cat doit lire les fichiers en séquence et écrire leur contenu sur la sortie standard dans la même séquence.

Je pense donc que concaténer signifie ici lire les fichiers dans l'ordre.

5
cuonglm

Utilisation de bash internes et évitant la création de sous-processus:

{ while IFS='' read -rd '' _bcat_; do printf '%s\0' "${_bcat_}"; done; printf '%s' "${_bcat_}"; unset _bcat_; } <'/path/to/file'

IFS ne sera appliqué qu'à la commande read, donc ne vous inquiétez pas de vos modifications globales IFS.

Boucle requise pour gérer les caractères nuls (merci à Stéphane Chazelas).

Cette méthode ne convient pas aux gros fichiers car le contenu du fichier est lu en premier dans la variable (donc la mémoire). BTW J'ai essayé d'imprimer un fichier texte de 39M de cette façon et l'utilisation de la mémoire bash n'a pas dépassé 5M, donc je ne suis pas sûr de ce cas.

Il est également sacrément lent et inefficace sur le processeur: pour le même fichier de 39 millions, cela a pris environ 3 minutes avec une utilisation à 100% d'un seul cœur.

Pour les gros fichiers ou les binaires, mieux utiliser cat '/path/to/file' ou même dd if='/path/to/file' bs=1M si possible.

4
Mikhail

Tout comme une démonstration, vous pouvez faire

cp foo /dev/stdout
2
wisbucky