gawk inplace et stdout

Est-il possible d'utiliser l'option -i inplace et d'imprimer aussi des choses à stdout ?

Par exemple, si je voulais mettre à jour un file, et s'il y a des modifications, imprimer le nom du file et les lignes modifiées en stderr je pourrais faire quelque chose comme

 find -type f -name 'myfiles' -exec gawk -i inplace '{if(gsub(/pat/, "repl")) { print FILENAME > "/proc/self/fd/2" ; print > "/proc/self/fd/2"; } print;}' {} + 

mais existe-t-il un moyen d'utiliser stdout place, ou un moyen plus propre d'imprimer ce bloc au stream alternatif?

Vous devriez utiliser /dev/stderr ou /dev/fd/2 au lieu de /proc/self/fd/2 . gawk gère /dev/fd/x et /dev/stderr par lui-même (que le système ait ces files ou non).

Quand vous faites un:

 print "x" > "/dev/fd/2" 

gawk fait une write(2, "x\n") , alors que quand vous le faites:

 print "x" > "/proc/self/fd/2" 

puisqu'il ne traite pas spécialement /proc/self/fd/x , il fait un:

 fd = open("/proc/self/fd/2", O_WRONLY|O_CREAT|O_TRUNC); write(fd, "x\n"); 

Premier /proc/self/fd est spécifique à Linux et sur Linux ils sont problématiques. Les deux versions ci-dessus ne sont pas équivalentes lorsque stderr est à un file normal ou autre, ou à un socket (pour lequel ce dernier échouerait) (sans oublier qu'il gaspille un descripteur de file).

Cela étant dit, si vous avez besoin d'écrire dans la sortie standard, vous devez la sauvegarder dans un autre file tel que:

 gawk -i inplace '{ print "goes into the-file" print "to stdout" > "/dev/fd/3"}' the-file 3>&1 

gawk redirige stdout avec in-place vers le file. C'est nécessaire parce que, par exemple, vous voudriez:

 awk -i inplace '{system("uname")}' file 

pour stocker la sortie uname dans le file .

Juste pour le plaisir, une approche alternative utilisant uniquement les fonctionnalités POSIX de find , sh , grep et (notamment) ex :

 find . -type f -name 'myfiles' -exec sh -c ' for f do grep -q "pat" "$f" && { printf "%s\n" "$f"; printf "%s\n" "%s/pat/repl/g" x | ex "$f"; }; done' find-sh {} + 

(Tous les sauts de ligne de la command ci-dessus sont facultatifs, ils peuvent être condensés en une seule ligne.)

Ou, sans doute plus lisiblement:

 find . -type f -name 'myfiles' -exec sh -c ' for f do if grep -q "pat" "$f"; then printf "%s\n" "$f" printf "%s\n" "%s/pat/repl/g" x | ex "$f" fi done' find-sh {} + 

🙂


(Cette command n'est pas testée, les modifications sont les bienvenues si je fais des modifications.)