Gawk en el lugar y stdout

10

¿Es posible usar gawkla -i inplaceopción y también imprimir cosas stdout?

Por ejemplo, si quisiera actualizar un archivo, y si hay algún cambio, imprima el nombre del archivo y las líneas cambiadas, stderrpodría hacer algo como

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;}' {} +

pero, ¿hay alguna forma de usarla stdouto una forma más limpia de imprimir ese bloque en la secuencia alternativa?

Eric Renouf
fuente
2
En este caso particular, es posible que desee ejecutar algo así find -type f -name 'myfiles' -exec grep -q 'pattern' {} \; -print -exec gawk -i inplace '{do_your_sub_and_print_to_dev/stderr_too}' {} \;, solo usará awk para editar los archivos que realmente contienen líneas que coinciden con ese patrón.
don_crissti
@don_crissti A menudo olvido cuánto más rápido greppuede ser. Mi primer instinto es evitar tener que "procesar" el archivo dos veces, pero no me sorprendería en absoluto si de todos modos es más rápido. Simplemente muestra por qué puedo confiar en mi instinto en las tareas de creación de perfiles
Eric Renouf
1
Sí, es bastante rápido y tenga en cuenta que: 1) si no hay coincidencia, solo procesa el archivo una vez (la parte -print -exec gawkya no se ejecuta) y 2) se detendrá en la primera coincidencia, a menos que la primera coincidencia esté en el última línea todavía no está procesando todo el archivo dos veces (es más como 1.X veces). Además, si gawk -i inplacefunciona así sed -i, editará el archivo en el lugar de todos modos, es decir, actualizar las marcas de tiempo, el inodo, etc., incluso si no hubiera nada que editar ...
don_crissti

Respuestas:

13

Deberías usar /dev/stderro en /dev/fd/2lugar de /proc/self/fd/2. gawkmaneja /dev/fd/xy /dev/stderrpor sí mismo (independientemente de si el sistema tiene esos archivos o no).

Cuando haces un:

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

gawkhace un write(2, "x\n"), mientras que cuando haces:

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

ya que no trata /proc/self/fd/xespecialmente, hace un:

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

Primero /proc/self/fdes específico de Linux y en Linux son problemáticos. Las dos versiones anteriores no son equivalentes cuando stderr corresponde a un archivo normal u otro que se puede buscar o a un socket (para el cual este último fallaría) (sin mencionar que desperdicia un descriptor de archivo).

Dicho esto, si necesita escribir en el stdout original, debe guardarlo en otro archivo como:

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

gawkredirecciona stdout con in situ al archivo. Es necesario porque, por ejemplo, querrías:

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

para almacenar la unamesalida en el file.

Stéphane Chazelas
fuente
2

Sólo por diversión, un enfoque alternativo utilizando sólo cuenta con POSIX de find, sh, grepy (sobre todo) 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 {} +

(Todos los saltos de línea en el comando anterior son opcionales; se puede condensar en una sola línea).

O, posiblemente más legible:

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 {} +

:)


(Este comando no ha sido probado; las ediciones son bienvenidas si hice alguna tontería).

Comodín
fuente