Enregistrer les modifications en place avec awk

135

J'apprends awket j'aimerais savoir s'il existe une option pour écrire les modifications dans un fichier, similaire à l' sedendroit où j'utiliserais l' -ioption pour enregistrer les modifications dans un fichier.

Je comprends que je pourrais utiliser la redirection pour écrire des modifications. Cependant, y a-t-il une option awkpour le faire?

Deano
la source
Voir également serverfault.com/a/547331/313521 pour la réponse plus générale à «l'édition d'un fichier en place avec redirection».
Wildcard
@Wildcard. La solution est horriblement fragile. Il n'y a absolument aucune garantie sur l'ordre des événements, et l'utilisation de cette solution peut tronquer vos données. En passant, je ne peux pas commenter ce site directement parce que j'ai besoin de 50 représentants sur ce site pour le faire. Je ne comprendrai jamais pourquoi SO fragmenté en Unix / Linux et en administration de serveur, et al. OMI, c'était une erreur.
William Pursell
@WilliamPursell, "aucune garantie sur l'ordre des événements" - c'est en fait faux. La seule fragilité de la solution est si la longueur du contenu est supérieure à la longueur maximale d'une commande. L'ordre des événements est cependant garanti.
Wildcard
@Wildcard Quelle norme garantit cette commande?
William Pursell
@WilliamPursell c'est garanti par la documentation bash. Pour d'autres obus, je ne sais pas. (En passant, si vous liez votre compte, vous aurez 100 bonus d'association de représentants et pourrez commenter.)
Wildcard

Réponses:

142

Dans la dernière version de GNU Awk (depuis la sortie de la version 4.1.0 ), il a l'option d' édition de fichier "inplace" :

[...] L'extension "inplace", construite à l'aide de la nouvelle fonction, peut être utilisée pour simuler la fonctionnalité GNU " sed -i". [...]

Exemple d'utilisation:

$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1 file2 file3

Pour conserver la sauvegarde:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/foo/, "bar") }
> { print }' file1 file2 file3
lind
la source
1
@sudo_O - Merci pour la démonstration "inplace". A voté votre réponse!
lind
On dirait que l'option a peut-être été supprimée? Avec la version 4.1.3, j'ai "-i includeefile --include = includeefile"
Keith Hughitt
1
@Keith J'ai eu la même question. Je viens de l'essayer et cela fonctionne sur mon 4.1.3. inplaceest en fait une bibliothèque incluse avec gawkselon la réponse de iiSeymour , il en inplaceest de même pour quelque chose qui peut être inclus en tant que fichierincludefile .
cxw le
Une mise en garde importante ici: le tableau «vu» se remplira de lignes dupliquées de TOUS les fichiers inclus dans la commande. Donc, si chaque fichier a par exemple un en-tête commun, celui-ci sera supprimé dans chaque fichier après le premier. Si vous souhaitez à la place traiter chaque fichier indépendamment, vous devrez faire quelque chose comme pour f dans * .txt; do gawk -i inplace '! vu [$ 0] ++' "$ f"; done
Nick K9 le
136

Sauf si vous avez GNU awk 4.1.0 ou version ultérieure ...

Vous n'aurez pas une telle option que l'option de sed, -ialors faites plutôt:

$ awk '{print $0}' file > tmp && mv tmp file

Remarque: ce -in'est pas magique, il crée également un fichier temporaire qui le sedgère juste pour vous.


À partir de GNU awk 4.1.0 ...

GNU awka ajouté cette fonctionnalité dans la version 4.1.0 (publiée le 10/05/2013) . Ce n'est pas aussi simple que de donner simplement l' -ioption décrite dans les notes publiées:

La nouvelle option -i (de xgawk) est utilisée pour charger les fichiers de bibliothèque awk. Cela diffère de -f en ce que le premier argument sans option est traité comme un script.

Vous devez utiliser le inplace.awkfichier d'inclusion fourni pour appeler l'extension correctement comme suit:

$ cat file
123 abc
456 def
789 hij

$ gawk -i inplace '{print $1}' file

$ cat file
123
456
789

La variable INPLACE_SUFFIXpeut être utilisée pour spécifier l'extension d'un fichier de sauvegarde:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{print $1}' file

$ cat file
123
456
789

$ cat file.bak
123 abc
456 def
789 hij

Je suis heureux de cette fonctionnalité a été ajoutée mais pour moi, la mise en œuvre est pas très awkish que le pouvoir vient de la concision de la langue et -i inplaceest de 8 caractères trop long imo .

Voici un lien vers le manuel du mot officiel.

Chris Seymour
la source
Votre «premier» exemple ne devrait-il pas ressembler davantage à awk '{ gsub(/foo/, "bar" ) } ; { print $0 }' file > tmp.txt && mv -v tmp.txt file:?
Tony Barganski
À ma grande surprise, en avril 2019, toujours à gawk 4.0.2. Ne laissez personne vous dire que telle ou telle version sera disponible.
John Lunzer
Litte plus court en awk '{print $0}' file | sponge fileutilisant spongede moreutils.
brablc
15

@sudo_O a la bonne réponse .

Cela ne peut pas fonctionner:

someprocess < file > file

Le shell effectue les redirections avant de céder le contrôle à un processus ( redirections ). La >redirection tronquera le fichier à une taille nulle ( redirection de la sortie ). Par conséquent, au moment où un processus est lancé et souhaite lire le fichier, il n'y a pas de données à lire.

Glenn Jackman
la source
14

juste un petit hack qui fonctionne

echo "$(awk '{awk code}' file)" > file
Yuri G.
la source
Fonctionne comme un charme! Mais est-il possible d'enregistrer la commande awk dans une variable et de l'utiliser simplement dans votre astuce?
ashrasmun
13

Une alternative consiste à utiliser sponge:

awk '{print $0}' your_file | sponge your_file

Où vous remplacez '{print $0}'par votre script awk et your_filepar le nom du fichier que vous souhaitez éditer sur place.

sponge absorbe entièrement l'entrée avant de l'enregistrer dans le fichier.

Codoscope
la source
À quel point l'éponge est-elle standard / portable?
Thomas
2
spongefait partie de moreutils. Il ne sera donc pas présent par défaut dans la plupart des systèmes. Mais on dirait qu'au moins spongelui-même est assez portable et peut être exécuté presque partout.
MarSoft
1
L'inconvénient de cette solution par rapport à tee-basée est qu'elle spongelit tout dans la RAM avant d'écrire, par conséquent, elle se fige sur les fichiers volumineux.
MarSoft
5

le suivi ne fonctionnera pas

echo $(awk '{awk code}' file) > file

cela devrait fonctionner

echo "$(awk '{awk code}' file)" > file
Flowmix Leonsio
la source
3

Si vous souhaitez une solution uniquement awk sans créer de fichier temporaire et utilisable avec la version! = (Gawk 4.1.0):

awk '{a[b++]=$0} END {for(c=0;c<=b;c++)print a[c]>ARGV[1]}' file
faucon
la source
4
Mais cela met-il en mémoire tampon le fichier entier? Prenons un fichier de 20 Go.
Amit Naidu
0

Utilisation du tee

 awk '{awk code}' file | tee file

la teecommande a lieu et est exécutée une fois la awkcommande terminée en raison du |.

Shaiki Siegal
la source
5
Ceci est une erreur. Les deux commandes sont exécutées en parallèle et les données sont immédiatement diffusées à travers le tuyau. Tout fichier plus grand que la mémoire tampon (8192 octets sur ma machine) sera tronqué et vous perdrez des données.
tripflag