Salve as modificações no local com o awk

135

Estou aprendendo awke gostaria de saber se existe uma opção para gravar alterações no arquivo, semelhante a sedonde eu usaria a -iopção para salvar modificações em um arquivo.

Entendo que eu poderia usar o redirecionamento para escrever alterações. No entanto, existe uma opção awkpara fazer isso?

Deano
fonte
Consulte também serverfault.com/a/547331/313521 para obter a resposta mais geral para "editar um arquivo no local com redirecionamento".
Curinga
@Wildcard. A solução é terrivelmente frágil. Não há absolutamente nenhuma garantia na ordem dos eventos, e o uso dessa solução pode truncar seus dados. Como um aparte, não posso comentar diretamente nesse site, porque preciso de 50 representantes nesse site. Eu nunca vou entender por que SO fragmentado em Unix / Linux e servidor admin, et al. OMI, isso foi um erro.
William Pursell
@WilliamPursell, "não há garantia na ordem dos eventos" - isso é realmente falso. A única fragilidade que a solução possui é se o comprimento do conteúdo for maior que o comprimento máximo de um comando. A ordem dos eventos, no entanto, é garantida.
Curinga
@Wildcard Que padrão garante esse pedido?
William Pursell
@WilliamPursell é garantido pela documentação do bash. Para outras conchas, não sei. (By the way, se você vincular sua conta, você terá bônus de associação 100 representante e será capaz de comentário.)
Wildcard

Respostas:

142

No GNU Awk mais recente (desde a versão 4.1.0 ), ele tem a opção de edição "inplace" de arquivos :

A extensão "inplace", criada usando a nova instalação, pode ser usada para simular o sed -irecurso " " GNU . [...]

Exemplo de uso:

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

Para manter o backup:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/foo/, "bar") }
> { print }' file1 file2 file3
lind
fonte
1
@sudo_O - Obrigado pela demonstração "local". Votou com sua resposta!
lind
Parece que a opção pode ter sido removida? No 4.1.3, tenho "-i includefile --include = includefile" #
Keith Hughitt
1
@ Keith eu tive a mesma pergunta. Eu apenas tentei e funciona no meu 4.1.3. inplaceé, na verdade, uma biblioteca incluída de gawkacordo com a resposta do iiSeymour , então inplaceé algo que pode ser incluído como um includefile.
Cxw 23/06
Uma ressalva importante aqui: o array 'visto' será preenchido com linhas duplicadas de TODOS os arquivos incluídos no comando. Portanto, se cada arquivo tiver, por exemplo, um cabeçalho comum, ele será removido em todos os arquivos após o primeiro. Se você deseja tratar cada arquivo de forma independente, precisará fazer algo como f em * .txt; gawk -i inplace '! seen [$ 0] ++' "$ f"; feito
Nick K9
136

A menos que você tenha o GNU awk 4.1.0 ou posterior ...

Você não terá essa opção como a opção sed, -ientão faça:

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

Nota: o -inão é mágico, ele também está criando um arquivo temporário, sedapenas lida com você.


A partir do GNU awk 4.1.0 ...

GNU awkadicionou essa funcionalidade na versão 4.1.0 (lançada em 10/05/2013) . Não é tão direto como apenas dar a -iopção conforme descrito nas notas liberadas:

A nova opção -i (do xgawk) é usada para carregar arquivos da biblioteca awk. Isso difere de -f, pois o primeiro argumento não opcional é tratado como um script.

Você precisa usar o inplace.awkarquivo de inclusão incluído para chamar a extensão corretamente da seguinte maneira:

$ cat file
123 abc
456 def
789 hij

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

$ cat file
123
456
789

A variável INPLACE_SUFFIXpode ser usada para especificar a extensão para um arquivo de backup:

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

$ cat file
123
456
789

$ cat file.bak
123 abc
456 def
789 hij

Estou feliz esse recurso foi adicionado, mas para mim, a implementação não é muito awkish como o poder vem da concisão da linguagem e -i inplaceé de 8 caracteres muito longo imo .

Aqui está um link para o manual da palavra oficial.

Chris Seymour
fonte
O seu 'primeiro' exemplo não deveria ser mais como awk '{ gsub(/foo/, "bar" ) } ; { print $0 }' file > tmp.txt && mv -v tmp.txt file:?
Tony Barganski #
Para minha surpresa, em abril de 2019, ainda no gawk 4.0.2. Não deixe ninguém lhe dizer que tal versão estará disponível.
John Lunzer
Litte mais curto awk '{print $0}' file | sponge fileusando spongefrom moreutils.
brablc 22/03
15

@sudo_O tem a resposta certa .

Isso não pode funcionar:

someprocess < file > file

O shell executa os redirecionamentos antes de transferir o controle para algum processo ( redirecionamentos ). O >redirecionamento truncará o arquivo para tamanho zero ( redirecionando a saída ). Portanto, quando algum processo é iniciado e deseja ler o arquivo, não há dados para ele ler.

Glenn Jackman
fonte
14

apenas um pequeno truque que funciona

echo "$(awk '{awk code}' file)" > file
Yuri G.
fonte
Funciona como um encanto! Mas é possível salvar o comando awk em variável e apenas usá-lo em seu truque bacana?
ashrasmun
13

Uma alternativa é usar sponge:

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

Onde você substitui '{print $0}'pelo seu script awk e your_filepelo nome do arquivo que deseja editar no local.

sponge absorve inteiramente a entrada antes de salvá-la no arquivo.

Codoscópio
fonte
Quão padrão / portátil é a esponja?
Thomas
2
spongefaz parte de moreutils. Portanto, ele não estará presente por padrão na maioria dos sistemas. Mas parece que pelo menos em spongesi é portátil o suficiente e pode ser executado em quase todos os lugares.
MarSoft 14/12
1
A desvantagem desta solução, comparada teecom a baseada em, é que spongeela lê tudo na RAM antes de escrever e, portanto, congela em arquivos grandes.
MarSoft 14/12
5

seguir não vai funcionar

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

isso deve funcionar

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

Caso você queira uma solução awk-only sem criar um arquivo temporário e utilizável com a versão! = (Gawk 4.1.0):

awk '{a[b++]=$0} END {for(c=0;c<=b;c++)print a[c]>ARGV[1]}' file
Falcão
fonte
4
Mas isso armazena o arquivo inteiro na memória? Considere um arquivo de 20 GB.
Amit Naidu
0

Usando tee

 awk '{awk code}' file | tee file

o teecomando ocorre e é executado após a conclusão do awkcomando devido ao |.

shaiki siegal
fonte
5
Isto está incorreto. Os dois comandos são executados em paralelo e os dados são transmitidos imediatamente através do canal. Qualquer arquivo maior que o buffer (8192 bytes na minha máquina) será truncado e você perderá dados.
tripflag