Il est bien connu qu'une commande comme celle-ci:
cat filename | some_sed_command >filename
efface le nom de fichier, car la redirection de sortie, exécutée avant la commande, entraîne la troncature du nom de fichier.
On pourrait résoudre le problème de la manière suivante:
cat file | some_sed_command | tee file >/dev/null
mais je ne suis pas sûr que cela fonctionnerait dans tous les cas: que se passe-t-il si le fichier (et le résultat de la commande sed) est très gros? Comment le système d'exploitation peut-il éviter d'écraser du contenu qui n'est toujours pas lu? Je vois qu'il y a aussi une commande éponge qui devrait fonctionner dans tous les cas: est-ce "plus sûr" que tee?
command-line
bash
tee
VeryHardCoder
la source
la source
Réponses:
Non .
Les chances
file
seront tronquées, mais aucune garantiecat file | some_sed_command | tee file >/dev/null
ne sera tronquéefile
.Tout dépend de la commande qui est traitée en premier, contrairement à ce que l'on peut attendre, les commandes d'un canal ne sont pas traitées de gauche à droite . Il n'y a aucune garantie quant à la commande qui sera choisie en premier, donc on pourrait tout aussi bien la considérer comme choisie au hasard et ne jamais compter sur le shell qui ne choisit pas la faute.
Étant donné que les chances que la commande incriminée soit choisie en premier entre trois commandes sont plus faibles que les chances que la commande incriminée soit choisie en premier entre deux commandes, il est moins probable qu'elle
file
soit tronquée, mais cela se produira toujours .script.sh
:Alors n'utilisez jamais quelque chose comme ça
cat file | some_sed_command | tee file >/dev/null
. Utilisezsponge
comme Oli l'a suggéré.Comme alternative, pour des environnements plus légers et / ou des fichiers relativement petits, on peut utiliser une chaîne ici et une substitution de commande pour lire le fichier avant d'exécuter une commande:
la source
Plus
sed
précisément, vous pouvez utiliser son-i
argument in situ. Il enregistre simplement dans le fichier qu'il a ouvert, par exemple:Si vous voulez faire quelque chose de plus costaud, en supposant que vous en fassiez plus
sed
, oui, vous pouvez mettre le tout en mémoire tamponsponge
(à partir dumoreutils
paquet) qui "absorbera" tout le stdin avant d'écrire dans le fichier. C'est commetee
mais avec moins de fonctionnalités. Cependant, pour une utilisation de base, c'est à peu près un remplacement sans rendez-vous:Est-ce que c'est plus sûr? Absolument. Il a probablement des limites, donc si vous faites quelque chose de colossal (et ne pouvez pas éditer sur place avec sed), vous voudrez peut-être apporter vos modifications à un deuxième fichier, puis
mv
ce fichier au nom de fichier d'origine. Cela devrait être atomique (donc tout ce qui dépend de ces fichiers ne se cassera pas s'ils ont besoin d'un accès constant).la source
Vous pouvez utiliser Vim en mode Ex:
%
sélectionner toutes les lignes!
Exécuter la commandex
Sauvegarder et quitterla source
Oh, mais ce
sponge
n'est pas la seule option; vous n'avez pas besoin de l'obtenirmoreutils
pour que cela fonctionne correctement. Tout mécanisme fonctionnera tant qu'il répond aux deux exigences suivantes:Vous voyez, le problème bien connu auquel l'OP fait référence est que le shell créera tous les fichiers nécessaires au fonctionnement des tuyaux avant même de commencer à exécuter les commandes dans le pipeline, c'est donc le shell qui tronque réellement le fichier de sortie (qui est malheureusement aussi le fichier d'entrée) avant même que l'une des commandes n'ait même eu la chance de commencer à s'exécuter.
La
tee
commande ne fonctionne pas, même si elle satisfait la première exigence, car elle ne satisfait pas la deuxième exigence: elle créera toujours le fichier de sortie immédiatement au démarrage, elle est donc essentiellement aussi mauvaise que la création d'un tuyau directement dans le fichier de sortie. (C'est en fait pire, car son utilisation introduit un délai aléatoire non déterministe avant que le fichier de sortie ne soit tronqué, vous pourriez donc penser que cela fonctionne, alors qu'en fait ce n'est pas le cas.)Donc, tout ce dont nous avons besoin pour résoudre ce problème est une commande qui tamponnera toutes ses entrées avant de produire une sortie, et qui est capable d'accepter le nom de fichier de sortie en tant que paramètre, de sorte que nous n'avons pas à diriger sa sortie dans le fichier de sortie. Une telle commande est
shuf
. Ainsi, ce qui suit accomplira la même chosesponge
:La
--random-source=/dev/zero
partie astucesshuf
pour faire son travail sans mélanger du tout, donc elle tamponnera votre entrée sans la modifier.la source