Comment exécuter une commande modifiant son fichier (argument) & # 8220; à la place & # 8221; en utilisant bash?

StackOverflow https://stackoverflow.com/questions/146435

  •  02-07-2019
  •  | 
  •  

Question

J'ai un fichier temp.txt que je souhaite trier avec la commande sort dans bash.

Je souhaite que les résultats triés remplacent le fichier d'origine.

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 pratique pour sort . 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?

Était-ce utile?

La solution

sort temp.txt -o temp.txt

Autres conseils

Un tri doit voir toutes les entrées avant de pouvoir sortir. Pour cette raison, le programme sort peut facilement offrir une option pour modifier un fichier sur place:

sort temp.txt -o temp.txt

Plus précisément, 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 toute sécurité à l'aide de commandes telles que sort -o F F et cat F | trier -o F . Cependant, sort avec - fusion ( -m ) peut ouvrir le fichier de sortie avant de lire toutes les entrées. Une commande telle que cat F | sort -m -o F - G n'est pas sans danger car sort pourrait commencer à écrire F avant que cat ne soit terminé.

Alors 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.

Les

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 sur place (et il leur serait plus difficile de prendre en charge cette fonctionnalité).

Vous travaillez généralement autour de cela avec un fichier temporaire ou, si vous voulez absolument éviter de créer 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 $/; 

Un tri doit voir toutes les entrées avant de pouvoir sortir. Pour cette raison, le programme sort peut facilement offrir une option pour modifier un fichier sur place:

sort temp.txt -o temp.txt

Plus précisément, 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 toute sécurité à l'aide de commandes telles que sort -o F F et cat F | trier -o F . Cependant, sort avec - fusion ( -m ) peut ouvrir le fichier de sortie avant de lire toutes les entrées. Une commande telle que cat F | sort -m -o F - G n'est pas sans danger car sort pourrait commencer à écrire F avant que cat ne soit terminé.

Alors 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.

Les

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 sur place (et il leur serait plus difficile de prendre en charge cette fonctionnalité).

Vous travaillez généralement autour de cela avec un fichier temporaire ou, si vous voulez absolument éviter de créer un fichier intermédiaire, vous pouvez utiliser un tampon pour stocker le résultat complet avant de l'écrire. Par exemple, avec perl :

<*>

Ici, la partie perl lit la sortie complète de uniq dans la variable $ _ , puis remplace le fichier d'origine par ces données. Vous pouvez faire la même chose dans le langage de script de votre choix, peut-être même en bash. Notez toutefois que vous aurez besoin de suffisamment de mémoire pour stocker l’ensemble du fichier, ce qui n’est pas conseillé lorsque vous travaillez avec des fichiers volumineux.

= <>; open(OUT,">temp.txt"); print OUT;'

Ici, la partie perl lit la sortie complète de uniq dans la variable $ _ , puis remplace le fichier d'origine par ces données. Vous pouvez faire la même chose dans le langage de script de votre choix, peut-être même en bash. Notez toutefois que vous aurez besoin de suffisamment de mémoire pour stocker l’ensemble du fichier, ce qui n’est pas conseillé lorsque vous travaillez avec des fichiers volumineux.

Voici une approche plus générale, fonctionne avec uniq, sort et tout le reste.

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

Le commentaire de Tobu sur Sponge donne à être une réponse à part entière.

Citation de la page d'accueil moreutils :

  

L’outil le plus couramment utilisé à moreutils jusqu’à présent est probablement sponge (1), qui vous permet de faire des choses comme celle-ci:

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

Cependant, sponge souffre du même problème Steve Jessop commente ici. S'il y en a un. des commandes dans le pipeline avant que sponge échoue, le fichier d'origine sera écrasé.

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

Uh-oh, mon-fichier-important est parti.

Voilà, une ligne:

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

Techniquement, il n’ya pas de copie dans un fichier temporaire et la commande "mv" doit être instantanée.

J'aime la réponse tri fichier -o fichier mais je ne souhaite pas taper le même nom de fichier deux fois.

Utilisation de BASH extension de l'historique :

$ sort file -o !#^

saisit le premier argument de la ligne en cours lorsque vous appuyez sur enter .

Un tri unique sur place:

$ sort -u -o file !#$

récupère le dernier argument de la ligne en cours.

Beaucoup ont mentionné l'option -o . Voici la partie de la page de manuel.

À partir de la page de manuel:

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.

Cela serait fortement contraint par la mémoire, mais vous pouvez utiliser awk pour stocker les données intermédiaires en mémoire, puis les réécrire.

uniq temp.txt | awk '{line[i++] = <*>}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt

Alternative à éponge avec le sed plus commun:

sed -ni r<(command file) file

Cela fonctionne pour n'importe quelle commande ( sort , uniq , tac , ...) et utilise le très connu sed L'option -i de de (éditer les fichiers sur place).

Avertissement: essayez d'abord le fichier de commande car la modification des fichiers sur place n'est pas sûre par nature.

Explication

Tout d'abord, vous dites à sed de ne pas imprimer les lignes (originales) ( -n ), et à l'aide du sed de commande r et bash de Substitution de processus , le contenu généré par < (fichier de commande) sera la sortie enregistrée en place .

Rendre les choses encore plus faciles

Vous pouvez intégrer cette solution à une fonction:

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

Exemple

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file

Utilisez l'argument - output = ou -o

J'ai juste essayé FreeBSD:

sort temp.txt -otemp.txt

Pour ajouter la fonctionnalité uniq , quels sont les inconvénients de:

sort inputfile | uniq | sort -o inputfile

Consultez l'éditeur non interactif, ex .

Si vous insistez pour utiliser le programme sort , vous devez utiliser un fichier intermédiaire. Je ne pense pas que sort propose une option de tri en mémoire. Toute autre astuce avec stdin / stdout échouera sauf si vous pouvez garantir que la taille de la mémoire tampon pour le tri de stdin est suffisamment grande pour contenir tout le fichier.

Edit: honte à moi. sort temp.txt -o temp.txt fonctionne parfaitement.

Une autre solution:

uniq file 1<> file
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top