it-swarm-eu.dev

Linux: comment utiliser un fichier en entrée et en sortie en même temps?

Je viens d'exécuter ce qui suit dans bash:

uniq .bash_history > .bash_history

et mon fichier historique s'est retrouvé complètement vide.

Je suppose que j'ai besoin d'un moyen de lire l'intégralité du fichier avant d'y écrire. Comment cela se fait-il?

PS: J'ai évidemment pensé à utiliser un fichier temporaire, mais je recherche une solution plus élégante.

59
MilliaLover

Je recommande d'utiliser sponge de moreutils . Depuis la page de manuel:

DESCRIPTION
  sponge  reads  standard  input  and writes it out to the specified file. Unlike
  a Shell redirect, sponge soaks up all its input before opening the output file.
  This allows for constructing pipelines that read from and write to the same 
  file.

Pour appliquer cela à votre problème, essayez:

uniq .bash_history | sponge .bash_history
49
jldugger

Je voulais juste apporter une autre réponse qui est simple et n'utilise pas d'éponge (car elle n'est souvent pas incluse dans les environnements légers).

echo "$(uniq .bash_history)" > .bash_history

devrait avoir le résultat souhaité. Le sous-shell est exécuté avant l'ouverture de .bash_history pour l'écriture. Comme expliqué dans la réponse de Phil P, au moment où .bash_history est lu dans la commande d'origine, il a déjà été tronqué par l'opérateur '>'.

84
Hart Simha

Le problème est que votre shell configure le pipeline de commandes avant d'exécuter les commandes. Ce n'est pas une question "d'entrée et de sortie", c'est que le contenu du fichier a déjà disparu avant même que uniq ne s'exécute. Cela va quelque chose comme:

  1. Le Shell ouvre le > fichier de sortie pour l'écriture, le tronquer
  2. Le shell se configure pour utiliser le descripteur de fichier 1 (pour stdout) pour cette sortie
  3. Le shell exécute uniq, peut-être quelque chose comme execlp ("uniq", "uniq", ".bash_history", NULL)
  4. uniq s'exécute, ouvre .bash_history et n'y trouve rien

Il existe différentes solutions, y compris l'édition sur place et l'utilisation de fichiers temporaires que d'autres mentionnent, mais la clé est de comprendre le problème, ce qui ne va vraiment pas et pourquoi.

13
Phil P

Une autre astuce pour ce faire, sans utiliser sponge, est la commande suivante:

{ rm .bash_history && uniq > .bash_history; } < .bash_history

C'est l'une des astuces décrites dans l'excellent article édition "sur place" des fichiers sur backreference.org.

Il ouvre essentiellement le fichier en lecture, puis le "supprime". Il n'est pas vraiment supprimé, cependant: il y a un descripteur de fichier ouvert qui le pointe, et tant qu'il reste ouvert, le fichier est toujours là. Ensuite, il crée un nouveau fichier avec le même nom et y écrit les lignes uniques.

Inconvénient de cette solution: si uniq échoue pour une raison quelconque, votre historique disparaîtra.

10
scy

utiliser une éponge de moreutils

uniq .bash_history | sponge .bash_history
6
Justin

Ce script sed supprime les doublons adjacents. Avec le -i option, il effectue la modification sur place. Il provient du fichier sedinfo:

sed -i 'h;:b;$b;N;/^\(.*\)\n\1$/ {g;bb};$b;P;D' .bash_history

En tant que friandise intéressante, sed utilise également un fichier temporaire (cela le fait juste pour vous):

$ strace sed -i 's/foo/bar/g' foo    
open("foo", O_RDONLY|O_LARGEFILE)       = 3
...
open("./sedPmPv9z", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
...
read(3, "foo\n"..., 4096)               = 4
write(4, "bar\n"..., 4)                 = 4
read(3, ""..., 4096)                    = 0
close(3)                                = 0
close(4)                                = 0
rename("./sedPmPv9z", "foo")            = 0
close(1)                                = 0
close(2)                                = 0

La description:
Le fichier temporaire ./sedPmPv9z devient fd 4 et les fichiers foo deviennent fd 3. Les opérations de lecture se font sur fd 3 et les écritures sur fd 4 (le fichier temporaire). Le fichier foo est alors remplacé par le fichier temporaire dans l'appel de renommage.

3
Kyle Brandt

Une autre solution:

uniq file 1<> líneas.txt
1
Antonio Lebrón

Vous pouvez utiliser Vim en mode Ex:

ex -sc '%!uniq' -cx .bash_history
  1. % sélectionner toutes les lignes

  2. ! exécuter la commande

  3. x enregistrer et fermer

0
Steven Penny

Un fichier temporaire est à peu près tout, à moins que la commande en question ne prenne en charge l'édition sur place (uniq ne le fait pas - certains seds le font (sed -i)).

0
Douglas Leeder