Commande de sortie du contenu du fichier vers stdout?

28

Je sais catque cela est possible, mais son objectif principal est de concaténer plutôt que de simplement afficher le contenu.

Je connais aussi lesset 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 il est fait spécialement pour cela, s'il y a une telle chose.

confused00
la source
1
En fait, 99% des fois que vous utilisez cat est pour afficher des fichiers plutôt que pour concaténer quoi que ce soit.
LatinSuD
1
@LatinSuD - 100% du temps - catconcatène.
mikeserv
@mikeserv: C'est une question de ce que vous entendez par effectuer une opération; une utilisation normale du langage impliquerait qu'elle soit effectuée au moins une fois. "Imprimer" une chaîne vide n'implique aucun caractère; il serait juste de dire qu'il n'imprime rien. Désormais, l'informatique a+b+cimplique d'effectuer 2 ajouts, et l'informatique an'implique rien. De même, l'exécution cat fn'implique aucune concaténation (même si c'est la seule chose que "concaténer une séquence d'un fichier" pourrait signifier).
Marc van Leeuwen
2
@mikeserv: Je ne comprends pas ce que vous entendez par in + out. Bien sûr, catlit un fichier et écrit un autre fichier (flux), mais cela ne signifie pas qu'il concatène quoi que ce soit. Si vous voulez peut-être dire que cat fc'est en train de faire cat - f, ce n'est tout simplement pas vrai.
Marc van Leeuwen
1
@ confused00: catétait vraiment destiné à concaténer ( cat file1 file2concaténera les deux fichiers à stdout). Mais son effet secondaire est que, lorsqu'il n'a qu'un seul fichier comme argument, il génère ce fichier sur stdout (où qu'il aille, soit votre terminal, soit redirigé vers quelque chose). Il n'y avait donc aucune autre commande créée juste pour sortir sur stdout, comme cela catexistait et le permettait simplement. Par conséquent, vous souhaitez utiliser cat.
Olivier Dulac

Réponses:

37

Le plus évident est cat. Mais, regardez aussi headet tail. Il y a aussi d' autres utillities shell pour imprimer une ligne de fichiers en 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. Je cours tous les creux stracepour 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 s'il a catété conçu pour concaténer des fichiers, il est le plus rapide et le plus efficace. sed, awket grepimprimé le fichier ligne par ligne, c'est pourquoi ils ont plus de 1275 appels système.

le chaos
la source
8
Bonne idée de compter les syscalls!
janvier
1
+1, la réponse est plus précise (sur la signification de chat), plus complète (alternatives) et recherchée (appels système)
Olivier Dulac
23

Je sais catque cela est possible, mais son objectif principal est de concaténer plutôt que de simplement afficher le contenu.

Le but de catest exactement cela, lire un fichier et sortir sur stdout.

Jan
la source
1
Mais cat --helpdit "Concaténer le (s) fichier (s), ou entrée standard, à la sortie standard". Je ne veux rien concaténer
confused00
18
Croyez-le ou non, le chat est exactement ce que vous cherchez de toute façon.
Jan
5
Non, @ confused00, Jan a raison. La chose est - le terminal est standard - vous voyez? faites readlink /dev/fd/1par exemple - vous devriez y trouver le nom de votre tty si vous l'exécutez à une invite standard. Donc, vous devez concaténer l'entrée à la sortie.
mikeserv du
2
@mikeserv Oui, je vois votre point, je suppose que j'étais trop fixé sur le sens de «concaténer».
confused00
3
La logique est que l'impression du contenu d' un fichier n'est qu'un cas particulier de l'impression séquentielle du contenu d' un ou plusieurs fichiers.
zwol
10

Tout d'abord, catécrit sur la sortie standard, qui n'est pas nécessairement un terminal, même s'il a caté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 cela catfait vraiment l'affaire, je suppose que vous ne posiez pas de question sur une telle situation.

Si votre objectif était d'envoyer ce qui est écrit sur la sortie standard dans un pipeline, alors l'utilisation catserait éligible au prix Useless Use of Cat , car cat file | pipeline(où pipelinesignifie n'importe quel pipeline) peut être fait plus efficacement <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 cattrop de temps 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 vous inquiétez de catpasser des cycles inutiles, vous ne devriez pas.

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

S'il y avait un nocatprogramme 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 demandez. 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 un seul argument et vous plaindre du contraire). Mais encore une fois cat, maintenant fourni avec un seul argument, fait exactement cela, donc il n'y a pas besoin de nocatprogramme.

Une fois que vous avez réussi à écrire le nocatprogramme, pourquoi s'arrêter à un seul argument? Envelopper le code dans une boucle for(;*argp!=NULL;++argp)n'est vraiment pas du tout un 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 catconcaténation de 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 catprogramme, ils ont ajouté quelques cloches et sifflets, car ils le font toujours. Mais l'essentiel est que l'aspect "concaténation" des catcoûts ne demande vraiment aucun effort, ni pour le programmeur ni pour la machine qui l'exécute. Le fait que catsubsume nullet nocatexplique la non-existence de tels programmes. Évitez d'utiliser catavec 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 à laquelle je suis lié admet qu'il s'agit d'une utilisation utile cat, alors n'hésitez pas.


Vous pouvez tester ce qui catest vraiment implémenté par une simple boucle autour d'une nocatfonctionnalité hypothétique , en appelant catavec plusieurs noms de fichiers parmi lesquels un nom invalide, pas en première position: plutôt que de vous plaindre tout de suite que ce fichier n'existe pas, catvide d'abord le précédent des fichiers valides, puis se plaint du fichier invalide (du moins c'est ainsi que mon chat se comporte).

Marc van Leeuwen
la source
7

Sous zshessai

<file

Je pense que c'est le moyen le plus court d'imprimer un fichier. Il utilise «caché» cat(ou moresi stdout est un terminal), mais la commande utilisée pour l'impression est contrôlée par une READNULLCMDvariable que vous pouvez écraser en toute sécurité directement par le nom de la commande 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
jimmij
la source
5

POSIX définit cat comme:

PRÉNOM

cat - concaténer et imprimer des fichiers

SYNOPSIS

chat [-u] [fichier ...]

LA 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 ici signifie lire les fichiers en séquence .

cuonglm
la source
5

Je sais que c'est une question du passé. Techniquement, puisque l'impression du contenu d'un fichier stdoutest une forme de concaténation, catest sémantiquement appropriée. N'oubliez pas que printfc'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)"
James M. Lay
la source
4
En plus d'être particulièrement détournée, la commande affichée n'est pas équivalente à cat file.txt, car elle supprimera tous les retours à la ligne (le $(...)fait).
Marc van Leeuwen
+1, belle prise. Ne savais pas ça.
James M. Lay
3

Utilisation des fonctions bashintégrées 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'

IFSsera appliqué uniquement à la readcommande, alors ne vous inquiétez pas de vos IFSmodifications globales .

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

Cette méthode n'est pas adaptée 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 binaires mieux utiliser cat '/path/to/file'ou même dd if='/path/to/file' bs=1Msi possible.

Mikhail
la source
1
Voir aussi pv -qqui sous Linux peut utiliser splice()lequel pour certains types de stdin / stdout améliorera les performances.
Stéphane Chazelas
1

Tout comme une démonstration, vous pouvez faire

cp foo /dev/stdout
wisbucky
la source