109 votes

Comment trier un fichier en place en utilisant le shell bash ?

J'ai un fichier temp.txt, que je veux trier avec la commande "sort" de bash.

Je veux que les résultats triés remplacent le fichier original.

Cela ne fonctionne pas par exemple (j'obtiens un fichier vide) :

sortx temp.txt > temp.txt

Peut-on faire cela en une seule ligne sans avoir recours à la copie dans des fichiers temporaires ?

EDIT : L'option -o est très intéressante pour le tri. J'ai utilisé "sort" dans ma question à titre d'exemple. Je rencontre le même problème avec d'autres commandes : uniq temp.txt > temp.txt. Existe-t-il une meilleure solution générale ?

171voto

daniels Points 6542
sort temp.txt -o temp.txt

29voto

Bruno De Fraine Points 11478

A sort doit voir toutes les entrées avant de pouvoir commencer à sortir. Pour cette raison, le sort peut facilement offrir une option permettant de modifier un fichier sur place :

sort temp.txt -o temp.txt

Plus précisément, le la documentation de GNU sort dit :

Normalement, sort lit toutes les entrées avant d'ouvrir le fichier de sortie. Vous pouvez donc trier un fichier en place en toute sécurité en utilisant des commandes telles que sort -o F F y cat F | sort -o F . Cependant, sort con --merge ( -m ) peut ouvrir le fichier de sortie avant de lire toutes les entrées, donc une commande comme cat F | sort -m -o F - G n'est pas sûr, car des personnes pourraient commencer à écrire F avant cat a fini de le lire.

Bien que la documentation de BSD sort dit :

Si [le] fichier de sortie est l'un des fichiers d'entrée, sort le copie dans un fichier temporaire avant de trier et d'écrire la sortie dans [le] fichier de sortie.

Des commandes telles que uniq peuvent commencer à écrire la sortie avant d'avoir fini de lire l'entrée. Ces commandes ne prennent généralement pas en charge l'édition in situ (et il serait plus difficile pour elles de prendre en charge cette fonctionnalité).

Vous pouvez généralement contourner ce problème avec un fichier temporaire ou, si vous voulez absolument éviter d'avoir un fichier intermédiaire, vous pouvez utiliser un tampon pour stocker le résultat complet avant de l'écrire. Par exemple, avec perl :

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

Ici, la partie perl lit la sortie complète de uniq en variable $_ puis écrase le fichier d'origine avec ces données. Vous pourriez faire la même chose dans le langage de script de votre choix, peut-être même en Bash. Mais notez qu'il faudra suffisamment de mémoire pour stocker le fichier entier, ce qui n'est pas conseillé lorsque vous travaillez avec des fichiers volumineux.

19voto

wor Points 133

Voici une approche plus générale, qui fonctionne avec uniq, sort et autres.

{ rm file && uniq > file ; } < fichier

10voto

Sean Points 3056

Commentaire de Tobu sur sponge les mandats étant une réponse à part entière.

Pour citer le moreutils page d'accueil :

Probablement l'outil le plus général de moreutils est sponge(1), qui vous permet de faire des choses comme ceci :

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

Cependant, sponge souffre du même problème Steve Jessop commente ici. Si l'une des commandes dans le pipeline avant sponge échoue, alors le fichier original sera écrasé.

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

Uh-oh, my-important-file est parti.

6voto

davr Points 9556

Voilà, une ligne :

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

Techniquement, il n'y a pas de copie dans un fichier temporaire, et la commande 'mv' devrait être instantanée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X