72 votes

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

Je viens d'exécuter ce qui suit en 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'ensemble du fichier avant d'y écrire. Comment faire ?

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

95voto

Hart Simha Points 1173
echo "$(uniq .bash_history)" > .bash_history

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

61voto

ChrisR Points 303

Je recommande d'utiliser sponge de moreutils . De 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

18voto

scy Points 307

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

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

Il s'agit de l'une des tricheries décrites dans l'excellent article Modification des fichiers "sur place". sur backreference.org.

En fait, il ouvre le fichier en lecture, puis le "supprime". Mais il n'est pas vraiment supprimé : Il y a un descripteur de fichier ouvert qui pointe vers lui, 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 histoire disparaîtra.

16voto

Phil P Points 3020

Le problème est que votre Shell met en place 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 est déjà parti avant même que uniq ne s'exécute. Cela donne quelque chose comme :

  1. Le Shell ouvre l'application > fichier de sortie pour l'écriture, en le tronquant
  2. Le Shell s'organise pour que le descripteur de fichier 1 (pour stdout) soit utilisé 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 plusieurs solutions, notamment l'édition sur place et l'utilisation de fichiers temporaires que d'autres ont mentionnées, mais l'essentiel est de comprendre le problème, ce qui ne va pas et pourquoi.

6voto

Justin Points 3736

Utiliser l'éponge de moreutils

uniq .bash_history | sponge .bash_history

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X