Comment puis-je utiliser un fichier dans une commande et redirect la sortie vers le même fichier sans le tronquer?

Fondamentalement, je veux prendre comme texte d’entrée d’un fichier, supprimer une ligne de ce fichier et renvoyer la sortie vers le même fichier. Quelque chose dans ce sens si cela rend les choses plus claires.

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name 

Cependant, lorsque je fais cela, je me retrouve avec un fichier vierge. Des pensées?

Vous ne pouvez pas faire cela car bash traite d’abord les redirections, puis exécute la commande. Donc, lorsque grep regarde nom_fichier, il est déjà vide. Vous pouvez cependant utiliser un fichier temporaire.

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > tmpfile cat tmpfile > file_name 

comme ça, envisagez d’utiliser mktemp pour créer le fichier tmpfile mais notez que ce n’est pas POSIX.

Utilisez une éponge pour ce genre de tâches. Sa part de moreutils.

Essayez cette commande:

  grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name 

Utilisez plutôt sed:

 sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name 

essayez ce simple

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name 

Votre fichier ne sera pas vide cette fois-ci 🙂 et votre sortie est également imprimée sur votre terminal.

Vous ne pouvez pas utiliser l’opérateur de redirection ( > ou >> ) sur le même fichier, car il a une priorité plus élevée et il va créer / tronquer le fichier avant même que la commande soit appelée. Pour éviter cela, vous devez utiliser des outils appropriés tels que tee , sponge , sed -i ou tout autre outil capable d’écrire des résultats dans le fichier (par exemple, sort file -o file ).

En gros, redirect les entrées vers le même fichier original n’a pas de sens et vous devez utiliser les éditeurs sur place appropriés pour cela, par exemple l’éditeur Ex (partie de Vim):

 ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name 

où:

  • '+cmd' / -c – lance toute commande Ex / Vim
  • g/pattern/d – supprime les lignes correspondant à un pattern en utilisant global ( help :g )
  • -s – mode silencieux ( man ex )
  • -c wq – exécute :write et :quit commandes

Vous pouvez utiliser sed pour obtenir la même chose (comme cela a déjà été montré dans d’autres réponses), mais in-place ( -i ) est une extension non standard de FreeBSD (peut fonctionner différemment entre Unix / Linux). un éditeur de fichiers. Voir: Le mode Ex a-t-il une utilisation pratique?

One Liner Alternative – Définit le contenu du fichier comme variable:

 VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name 

Il y a aussi ed (comme alternative à sed -i ):

 # cf. http://wiki.bash-hackers.org/howto/edit-ed printf '%s\n' H 'g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' wq | ed -s file_name 

Vous pouvez le faire en utilisant la substitution de processus .

C’est un peu un bidouillage car bash ouvre tous les canaux de manière asynchrone et nous devons contourner ce problème en utilisant sleep ainsi YMMV.

Dans votre exemple:

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name) 
  • >(sleep 1 && cat > file_name) crée un fichier temporaire qui reçoit la sortie de grep
  • sleep 1 retards pendant une seconde pour donner le temps à grep d’parsingr le fichier d’entrée
  • finally cat > file_name écrit la sortie

Vous pouvez utiliser slurp avec POSIX Awk:

 !/seg[0-9]\{1,\}\.[0-9]\{1\}/ { q = q ? q RS $0 : $0 } END { print q > ARGV[1] } 

Exemple

J’utilise habituellement le programme de départ pour faire ceci:

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name 

Il crée et supprime un fichier temporaire par lui-même.

Essaye ça

 echo -e "AAA\nBBB\nCCC" > testfile cat testfile AAA BBB CCC echo "$(grep -v 'AAA' testfile)" > testfile cat testfile BBB CCC 

peut-être que vous pouvez le faire comme ceci:

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | cat > file_name