Pourquoi la redirection de la sortie d'un fichier vers lui-même produit-elle un fichier vierge?
Dit dans Bash, pourquoi
less foo.txt > foo.txt
et
fold foo.txt > foo.txt
produire un vide foo.txt
? Puisqu'un ajout tel que less eggs.py >> eggs.py
produit deux copies du texte dans eggs.py
, on peut s'attendre à ce qu'un écrasement produise une copie du texte.
Remarque, je ne dis pas qu'il s'agit d'un bogue, il s'agit plutôt d'un pointeur vers quelque chose de profond à propos d'Unix.
bash
unix
unix-utils
seewalker
la source
la source
Réponses:
Lorsque vous utilisez
>
, le fichier est ouvert en mode troncature de sorte que son contenu est supprimé avant que la commande ne tente de le lire.Lorsque vous utilisez
>>
, le fichier est ouvert en mode ajout afin que les données existantes soient préservées. Il est cependant encore assez risqué d'utiliser le même fichier en entrée et en sortie dans ce cas. Si le fichier est suffisamment volumineux pour ne pas correspondre à la taille du tampon d'entrée en lecture, sa taille peut augmenter indéfiniment jusqu'à ce que le système de fichiers soit plein (ou que votre quota de disque soit atteint).Si vous souhaitez utiliser un fichier à la fois en entrée et en sortie avec une commande qui ne prend pas en charge la modification sur place, vous pouvez utiliser quelques solutions:
Utilisez un fichier intermédiaire et écrasez celui d'origine une fois terminé et seulement si aucune erreur ne s'est produite lors de l'exécution de l'utilitaire (c'est le moyen le plus sûr et le plus courant).
Évitez le fichier intermédiaire au détriment d'une perte de données partielle ou complète potentielle en cas d'erreur ou d'interruption. Dans cet exemple, le contenu de
foo.txt
est transmis en entrée à un sous - shell (à l'intérieur des parenthèses) avant la suppression du fichier. L'inode précédent reste en vie pendant que le sous-shell le maintient ouvert pendant la lecture des données. Le fichier écrit par l'utilitaire interne (icifold
) avec le même nom (foo.txt
) pointe vers un inode différent parce que l'ancienne entrée de répertoire a été supprimée donc techniquement, il y a deux "fichiers" différents avec le même nom pendant le processus. Lorsque le sous-shell se termine, l'ancien inode est libéré et ses données sont perdues. Veillez à vous assurer que vous disposez de suffisamment d'espace pour stocker temporairement à la fois l'ancien fichier et le nouveau, sinon vous perdrez des données.la source
sponge
de moreutils peut également aider.fold foo.txt | sponge foo.txt
- oufold foo.txt | sponge !$
devrait aussi faire.Le fichier est ouvert pour l'écriture par le shell avant que l'application ne puisse le lire. L'ouverture du fichier en écriture le tronque.
la source
En bash, l'opérateur de redirection de flux
... > foo.txt
se videfoo.txt
avant d'évaluer l'opérande gauche .On peut utiliser la substitution de commandes et imprimer son résultat comme solution de contournement. Cette solution prend moins de caractères supplémentaires que dans les autres réponses:
Attention: cette commande ne conserve aucun retour à la ligne en fin de ligne
foo.txt
. Jetez un œil dans la section commentaire ci-dessous pour plus d'informationsIci, le sous
$(...)
- shell est évalué avant l'opérateur de redirection de flux>
, d'où la conservation des informations.la source
tmp=$(cmd; printf q); printf '%s' "${tmp%q}"
. Mais vous avez manqué un autre problème avec cette réponse: il dit "subshell" quand il signifie "substitution de commande". Oui, les substitutions de commandes sont généralement des sous-coquilles, mais pas l'inverse, et les sous-coquilles, en général, ne sont d'aucune aide pour ce problème.