gawk inplace e stdout

9

É possível usar a opção gawk de -i inplace e também imprimir coisas para stdout ?

Por exemplo, se eu quiser atualizar um arquivo, e se houver alguma alteração, imprima o nome do arquivo e as linhas alteradas para stderr eu poderia fazer 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;}' {} +

mas existe uma maneira de usar stdout ou uma maneira mais limpa de imprimir esse bloco no fluxo alternativo?

    
por Eric Renouf 07.11.2016 / 18:02

2 respostas

13

Você deve usar /dev/stderr ou /dev/fd/2 em vez de /proc/self/fd/2 . gawk manipula /dev/fd/x e /dev/stderr por si mesmo (independentemente de o sistema ter ou não esses arquivos).

Quando você faz um:

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

gawk faz um write(2, "x\n") , enquanto quando você faz isso:

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

como não trata /proc/self/fd/x especialmente, faz um:

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

Primeiro /proc/self/fd é específico do Linux e no Linux eles são problemáticos. As duas versões acima não são equivalentes quando stderr é para um arquivo regular ou outro procurável ou para um socket (para o qual o último falharia) (sem mencionar que ele desperdiça um descritor de arquivo).

Dito isto, se você precisa escrever para o stdout original, você precisa salvá-lo em outro fd como:

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

gawk redireciona o stdout in-loco para o arquivo. É necessário porque, por exemplo, você deseja:

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

para armazenar a saída uname no file .

    
por 07.11.2016 / 18:17
2

Apenas por diversão, uma abordagem alternativa usando apenas os recursos POSIX de find , sh , grep e (mais notavelmente) 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 {} +

(Todas as quebras de linha no comando acima são opcionais; podem ser condensadas em uma linha única).

Ou, sem dúvida, de forma mais legível:

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 não foi testado; as edições serão bem-vindas se eu fizer algum erro).

    
por 08.11.2016 / 04:20

Tags