Comment fonctionne `cat <> file`?

42

cat < fileimprime le contenu du fichier sur la sortie standard.

cat > filelit stdin jusqu'à ce que Ctrl+ Dsoit détecté et que le texte saisi soit écrit dans le fichier .

cat <> file, du moins dans ma version de Bash, imprime le contenu du fichier avec bonheur (sans erreur), mais ne modifie pas le fichier et ne met pas à jour l’horodatage de la modification.

Comment la norme Bash justifie l'apparemment ignoré >dans la troisième déclaration - et, plus important encore , est - il fait quoi que ce soit?

Qix
la source

Réponses:

47

Bash utilise <>pour créer un descripteur de fichier en lecture-écriture :

L'opérateur de redirection

[n]<>word

provoque l'ouverture du fichier dont le nom est le développement du mot en lecture et en écriture sur le descripteur de fichier n ou sur le descripteur de fichier 0 si n n'est pas spécifié. Si le fichier n'existe pas, il est créé.

cat <> fileouvre en filelecture-écriture et le lie au descripteur 0 (entrée standard). Cela équivaut pour l'essentiel à < filetout programme judicieusement écrit, car personne n'est susceptible d'essayer d'écrire en entrée standard, mais s'il le pouvait, il le pourrait.

Vous pouvez écrire un programme simple C pour vérifier que directement - write(0, "hello", 6)rédigera hellodans l' fileentrée standard.

<>devrait également fonctionner dans tout autre shell compatible POSIX avec le même effet.

Michael Homer
la source
1
Ecrire ... en stdin? ... Existe-t-il un cas d'utilisation valable ?
Qix
3
D'un autre côté, je ne peux en penser aucun bon. Donner un descripteur explicite ( 4<>file) est utile, et je suppose que 0 est un aussi bon choix par défaut que n'importe quel autre. Lire de stdout n'est pas mieux.
Michael Homer
5
<>est également utile sur certains systèmes (comme Linux) pour ouvrir des canaux nommés sans les bloquer jusqu’à ce qu’un autre processus l’ouvre en écriture.
Stéphane Chazelas
1
@ Qix: Bien écrire (0, "Mot de passe:", 10) est un bon moyen de demander un mot de passe si vous avez l’intention de demander une adresse semblable à un tty. Je suis habitué à le voir uniquement sur stderr mais aucune raison en particulier que la même technique ne fonctionne pas sur stdin.
Josué
3
@ Qix - du raisonnement POSIX - L' <>opérateur pourrait être utile pour écrire une application fonctionnant avec plusieurs terminaux et souhaitant parfois démarrer un shell. Ce shell ne pourrait à son tour pas exécuter d’applications exécutées à partir d’un terminal de contrôle ordinaire, à moins de pouvoir utiliser <>... comme le pageur more, qui lit les erreurs standard pour obtenir ses commandes, ainsi les entrées et sorties standard sont tous deux disponibles pour leur usage habituel. cat food | more - >/dev/tty03 2<>/dev/tty03
mikeserv
38

<> fileouvre le fichier (sur le descripteur de fichier 0 (stdin) par défaut, comme <) en mode lecture + écriture sans troncature et en créant le fichier s'il n'existait pas auparavant .

Cela correspond aux O_RDWR|O_CREATindicateurs passés à l' open()appel système. Par contraste <est O_RDONLYet >est O_WRONLY|O_CREAT|O_TRUNCet >> O_WRONLY|O_CREAT|O_APPEND.

Avoir stdin en écriture n'est pas souvent utile car les applications n'écrivent généralement pas sur leur stdin. Les applications ne s’attendent généralement pas à lire et à écrire sur un descripteur de fichier qu’elles reçoivent au démarrage; ils lisent généralement depuis stdin (ou un descripteur de fichier qu'ils ouvrent eux-mêmes) et écrivent dans stdout ou stderr (ou un descripteur de fichier qu'ils s'ouvrent eux-mêmes).

<> peut avoir ses utilisations:

  • Vous préférerez peut-être cat <> fileplus cat < filesi vous ne voulez pas que la commande échoue si elle filen'existe pas, mais un vide filecréé à la place.
  • L'aspect non tronquant de <>rend utile d'écraser les fichiers en place. Cependant, dans ce cas, vous ne l'utilisez généralement pas sur le descripteur de fichier 0:

    printf xxx 1<> file

    remplace les 3 premiers octets de fileavec xxx.

  • Sur certains systèmes tels que Linux, <>sur un canal nommé (FIFO), il ouvre le canal nommé sans le bloquer (sans attendre qu'un autre processus ouvre l'autre extrémité) et s'assure que la structure du canal reste active. Par exemple dans:

    mkfifo pipe; sed 's/foo/bar/g' <> pipe

    sedgère les données entrantes à partir de n'importe quel nombre de processus écrivant dessus et ne voit jamais eof.

Stéphane Chazelas
la source
1
Notez que sur AT & T ksh93, la valeur par <>défaut est 1<>(stdout) au lieu de 0<>(stdin). C’est un bogue de conformité POSIX que j’ai signalé et qui sera corrigé dans la prochaine version. github.com/att/ast/issues/75 Mais jusqu'à ce que les versions actuelles de ksh93 deviennent inutilisables, vous devez inclure le numéro du descripteur de fichier à utiliser de manière <>portable.
Martijn Dekker
@ MartijnDekker, je sais, c'est moi qui t'en ai parlé en premier lieu ;-). Notez que cela ne concerne que ksh93t + (où le comportement a changé) et les versions ultérieures.
Stéphane Chazelas
Quels sont (ou étaient) les systèmes contrairement à Linux où mkfifo fifo; exec 3<>fifobloquerait?
Oncle Billy