Est-il sûr de rediriger stdout et stderr vers le même fichier sans copie de descripteur de fichier?

27

Je commence dans un répertoire vide.

$ touch aFile
$ ls
aFile

Ensuite, j'ai lsdeux arguments, dont l'un n'est pas dans ce répertoire. Je redirige les deux flux de sortie vers un fichier nommé output. J'utilise >>afin d'éviter d'écrire simultanément.

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile

Ce qui semble fonctionner. Y a-t-il des dangers à cette approche?

exit_status
la source
6
C'était un vote rapide. Cela a pris environ cinq secondes. Pouvez-vous me dire comment est-ce que vous pouvez évaluer la valeur de ma question si rapidement? Et mieux encore, qu'est-ce qui ne va pas pour que je puisse l'améliorer?
exit_status
Pourquoi n'utilisez-vous pas le plus standard ls aFile not_exist &>>outputici? (Remarque, je suppose que vous utilisez bash .)
FedonKadifeli
5
Parce que cela ne m'aide pas à comprendre ce que je demande. Je sais comment rediriger ces flux vers le même fichier, même de manière portable. Ce que je veux savoir, c'est s'il y a un problème avec ce que j'ai suggéré dans la question. @FedonKadifeli
exit_status
1
@FedonKadifeli &>>n'est PAS standard. C'est une syntaxe obsolète et ambiguë qui fonctionne différemment dans différents shells. Je me demande d'où vous tirez vos affaires.
Oncle Billy
4
Bash n'est pas une norme . Le mandat POSIX standard ls &>>foo ...doit être analysé comme deux commandes ls &et >>foo ..., et c'est ainsi que d'autres shells comme /bin/shUbuntu le analysent. Pour qu'il soit obsolète, vous pouvez regarder ici - bien que je ne prétende pas que ce soit une sorte d'autorité. bashCependant, vous pouvez demander aux responsables s'ils considèrent que c'est une bonne idée.
Oncle Billy

Réponses:

22

Non, ce n'est pas aussi sûr que la norme >>bar 2>&1.

Quand tu écris

foo >>bar 2>>bar

vous ouvrez le barfichier deux fois avec O_APPEND, créant deux objets de fichier complètement indépendants [1], chacun avec son propre état (pointeur, modes ouverts, etc.).

Ceci est très différent de celui 2>&1qui appelle simplement l'appel dup(2)système et crée les alias interchangeables stderr et stdout pour le même objet fichier.

Maintenant, il y a un problème avec ça:

O_APPENDpeut conduire à des fichiers corrompus sur les systèmes de fichiers NFS si plusieurs processus ajoutent des données à un fichier à la fois. Cela est dû au fait que NFS ne prend pas en charge l'ajout à un fichier, le noyau client doit donc le simuler, ce qui ne peut pas être fait sans condition de concurrence critique.

Vous pouvez généralement compter sur la probabilité du fichier comme baren foo >>bar 2>&1cours d' écriture en même temps de deux endroits séparés étant assez faible. Mais par vous, >>bar 2>>barvous venez de l'augmenter d'une douzaine d'ordres de grandeur, sans aucune raison.

[1] "Open File Descriptions" dans le jargon POSIX.

mosvy
la source
3
Formellement, pour les fichiers en mode ajout, c'est sûr . Le problème cité est un bogue dans NFS qui le rend inadapté (non conforme à POSIX) en tant que système de fichiers. Dans le cas du mode sans ajout, cependant, ce n'est jamais sûr.
R ..
1
C'est sans importance. Le double ajout de l'OP n'est pas sûr à utiliser (en plus d'être complètement inutile). Et O_APPENDc'est une sorte de raté de toute façon - assez onéreux à mettre en œuvre correctement.
mosvy
Je crois que la condition de concurrence NFS est uniquement entre différents clients. L'OS client doit coordonner toutes les écritures entre ses processus.
Barmar
@Barmar, ce serait vrai si le système d'exploitation client ne se souciait que de sa propre vue d'un fichier nfs. Mais lors de l'écriture dans un fichier nfs ouvert avec O_APPEND, le client récupérera d'abord la "vraie" taille du fichier sur le serveur ("revalidera" l'inode) puis fera la recherche + écriture + mise à jour de l'inode en cache, et seule la dernière partie sera fait sous des verrous, ce qui signifie que la première partie pourrait toujours récupérer une taille périmée à partir du serveur et remplacer la bonne à partir de l'inode local / mis en cache. Même problème avec lseek(SEEK_END).
mosvy
Je ne vois toujours pas comment cela pourrait provoquer des conditions de concurrence entre deux flux sur le même client. Les deux flux doivent faire référence au même inode mis en cache local.
Barmar
22

Que se passe-t-il lorsque vous le faites

some_command >>file 2>>file

est que filesera ouvert pour être ajouté deux fois. Ceci est sûr à faire sur un système de fichiers POSIX. Toute écriture qui arrive au fichier lorsqu'il est ouvert pour l'ajout se produira à la fin du fichier, que les données proviennent du flux de sortie standard ou du flux d'erreur standard.

Cela repose sur la prise en charge des opérations d’écriture d’ajout atomique dans le système de fichiers sous-jacent. Certains systèmes de fichiers, tels que NFS, ne prennent pas en charge l'ajout atomique. Voir par exemple la question "Le fichier est-il atomique sous UNIX?" Sur StackOverflow.

En utilisant

some_command >>file 2>&1

fonctionnerait même sur NFS cependant.

Cependant, en utilisant

some_command >file 2>file

n'est pas sûr, car le shell tronquera le fichier de sortie (deux fois) et toute écriture qui se produira sur l'un ou l'autre flux écrasera les données déjà écrites par l'autre flux.

Exemple:

$ { echo hello; echo abc >&2; } >file 2>file
$ cat file
abc
o

La hellochaîne est écrite en premier (avec un retour à la ligne de fin), puis la chaîne abcsuivie d'un retour à la ligne est écrite à partir de l'erreur standard, écrasant le hell. Le résultat est la chaîne abcavec une nouvelle ligne, suivie de ce qui reste de la première echosortie, une oet une nouvelle ligne.

L'échange des deux echoautour de la plaie ne produit que hellodans le fichier de sortie car cette chaîne est écrite en dernier et est plus longue que la abcchaîne. L'ordre dans lequel les redirections se produisent n'a pas d'importance.

Il serait préférable et plus sûr d'utiliser le plus idiomatique

some_command >file 2>&1
Kusalananda
la source
1
Bien que cela soit vrai pour les coquilles modernes, ce n'était pas le cas dans la coquille Bourne ou Thomson (d'où >>vient), où >>s'ouvrirait pour l'écriture et chercherait à la fin (je suppose que O_APPEND n'a pas encore été inventé à l'époque). Même sur Solaris 10, /bin/sh -c '(echo a; echo b >&2) >> file 2>> file; cat file'sorties b.
Stéphane Chazelas
@ StéphaneChazelas Est-ce un problème avec l'implémentation de Solaris 10 shou avec son système de fichiers?
Kusalananda
1
C'est ce qui se >>faisait à l'origine, il ne s'ouvrait pas avec O_APPEND, il s'ouvrait sans et cherchait jusqu'à la fin. Ce n'est pas vraiment un problème, c'est ce qu'il faisait et a été documenté.
Stéphane Chazelas
0

Cela dépend de ce que vous voulez réaliser. C'est à vous de décider si vous pouvez avoir des erreurs dans le même fichier que la sortie. Il s'agit simplement d'enregistrer du texte dans un fichier avec les fonctionnalités du shell qui vous permettent de rediriger comme vous le souhaitez. Il n'y a pas de oui ou de non absolu. Comme tout sous Linux, cela peut se faire de plusieurs manières, c'est ma façon de ls notExistingFile existingFile >> output 2>&1 répondre à la question: en termes de redirection elle-même, oui, c'est parfaitement sûr.

ange
la source
Il y a plus que ce que vous dites ici. Le même exercice avec >au lieu de >>remplacera certains caractères. Ce n'est donc pas seulement que le shell me permet de rediriger, car lorsque je redirige avec >, le résultat est différent. Il y a donc des nuances avec >, y en a-t-il avec >>?
exit_status
Oui, ce sera différent. Comme je l'ai dit, cela dépend de votre objectif >- écraser. >>- ajouter
Angel