gawk inplace et stdout

10

Est-il possible d'utiliser gawkl' -i inplaceoption de et d'imprimer également des choses sur stdout?

Par exemple, si je voulais mettre à jour un fichier et s'il y a des changements, imprimez le nom du fichier et les lignes modifiées pour que stderrje puisse 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 à la stdoutplace, ou un moyen plus propre d'imprimer ce bloc sur le flux alternatif?

Eric Renouf
la source
2
Dans ce cas particulier, vous voudrez peut-être exécuter quelque chose comme find -type f -name 'myfiles' -exec grep -q 'pattern' {} \; -print -exec gawk -i inplace '{do_your_sub_and_print_to_dev/stderr_too}' {} \;ça, vous n'utiliserez awk que pour modifier les fichiers qui contiennent réellement des lignes correspondant à ce modèle.
don_crissti
@don_crissti J'oublie souvent combien plus rapide greppeut être. Mon premier réflexe est d'éviter de devoir "traiter" deux fois le fichier, mais je ne serais pas du tout surpris s'il est en effet plus rapide néanmoins. Cela montre simplement pourquoi je peux faire confiance à mon instinct pour les tâches de profilage
Eric Renouf
1
Oui, c'est assez rapide et gardez à l'esprit que: 1) s'il n'y a pas de correspondance, vous ne traitez le fichier qu'une seule fois (la partie avec -print -exec gawkn'est plus exécutée) et 2) il s'arrêtera au 1er match, donc à moins que le 1er match ne soit sur le dernière ligne, vous ne traitez toujours pas le fichier entier deux fois (c'est plus comme 1.X fois). De plus, si cela gawk -i inplacefonctionne, sed -ile fichier sera modifié sur place de toute façon - c'est-à-dire la mise à jour des horodatages, de l'inode, etc. même s'il n'y avait rien à modifier ...
don_crissti

Réponses:

13

Vous devez utiliser /dev/stderrou /dev/fd/2au lieu de /proc/self/fd/2. gawkgère /dev/fd/xet /dev/stderrpar lui-même (que le système possède ou non ces fichiers).

Quand vous faites:

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

gawkfait un write(2, "x\n"), tandis que lorsque vous faites:

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

comme il ne traite pas /proc/self/fd/xspécialement, il fait:

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

Le premier /proc/self/fdest spécifique à Linux et sur Linux, il est problématique. Les deux versions ci-dessus ne sont pas équivalentes lorsque stderr est vers un fichier normal ou autre recherchable ou vers un socket (pour lequel ce dernier échouerait) (sans oublier qu'il gaspille un descripteur de fichier).

Cela étant dit, si vous devez écrire sur la sortie standard, vous devez l'enregistrer dans un autre fd comme:

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

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

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

pour stocker la unamesortie dans le file.

Stéphane Chazelas
la source
2

Juste pour le plaisir, une autre approche utilisant uniquement des caractéristiques POSIX find, sh, grepet (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 dans la commande ci-dessus sont facultatifs; il peut être condensé en une seule ligne.)

Ou, sans doute plus lisible:

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 commande n'est pas testée; les modifications sont les bienvenues si je fais des gaffes.)

Caractère générique
la source