awk 'processing_script_here' my=file.txt
parece parar e esperar indefinidamente ...
O que está acontecendo aqui e como faço para funcionar?
awk 'processing_script_here' my=file.txt
parece parar e esperar indefinidamente ...
O que está acontecendo aqui e como faço para funcionar?
Respostas:
Como Chris diz , os argumentos do formulário variablename=anything
são tratados como atribuição de variável (executada no momento em que os argumentos são processados, em oposição aos (mais recentes) -v var=value
executados antes das BEGIN
instruções), em vez de nomes de arquivo de entrada.
Isso pode ser útil em coisas como:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Onde você pode especificar um arquivo diferente FS
/ RS
por. Também é comumente usado em:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Qual é uma versão mais segura de:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(que não funciona se file1
estiver vazio)
Mas isso atrapalha quando você tem arquivos cujo nome contém =
caracteres.
Agora, isso é apenas um problema quando o que resta do primeiro =
é um awk
nome de variável válido .
O que constitui um nome de variável válido em awk
é mais rigoroso que em sh
.
O POSIX exige que seja algo como:
[_a-zA-Z][_a-zA-Z0-9]*
Com apenas caracteres do conjunto de caracteres portátil. No entanto, o /usr/xpg4/bin/awk
Solaris 11 pelo menos não é compatível nesse sentido e permite que caracteres alfabéticos no código do idioma nos nomes de variáveis, não apenas a-zA-Z.
Portanto, um argumento como x+y=foo
ou =bar
ou ./foo=bar
ainda é tratado como um nome de arquivo de entrada e não como uma atribuição, pois o que resta do primeiro =
não é um nome de variável válido. Um argumento como Stéphane=Chazelas.txt
pode ou não, dependendo da awk
implementação e da localidade.
Por isso, com o awk, é recomendável usar:
awk '...' ./*.txt
ao invés de
awk '...' *.txt
por exemplo, para evitar o problema se você não puder garantir que o nome dos txt
arquivos não contenha =
caracteres.
Além disso, lembre-se de que um argumento como esse -vfoo=bar.txt
pode ser tratado como uma opção se você usar:
awk -f file.awk -vfoo=bar.txt
(aplica-se também para awk '{code}' -vfoo=bar.txt
com o awk
de versões busybox antes 1.28.0, ver relatório de erros correspondente ).
Novamente, o uso de ./*.txt
soluções para isso (o uso de um ./
prefixo também ajuda com um arquivo chamado -
que, de outra forma, awk
entende como significando entrada padrão ).
É por isso também
#! /usr/bin/awk -f
shebangs realmente não funcionam. Enquanto esses var=value
podem ser contornados, fixe os ARGV
valores (adicione um ./
prefixo) em uma BEGIN
instrução:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
Isso não ajudará com as opções, pois elas são vistas awk
e não o awk
script.
Um problema cosmético em potencial com o uso desse ./
prefixo é que ele acaba FILENAME
, mas você sempre pode usá substr(FILENAME, 3)
-lo para removê-lo, se não quiser.
A implementação do GNU awk
corrige todos esses problemas com sua -E
opção.
Depois -E
, o gawk espera apenas o caminho do awk
script (onde -
ainda significa stdin) e, em seguida, uma lista apenas dos caminhos do arquivo de entrada (e, nem mesmo -
é tratado especialmente).
Foi especialmente desenvolvido para:
#! /usr/bin/gawk -E
shebangs onde a lista de argumentos sempre são arquivos de entrada (observe que você ainda pode editar essa ARGV
lista em uma BEGIN
instrução).
Você também pode usá-lo como:
gawk -e '...awk code here...' -E /dev/null *.txt
Usamos -E
um script vazio ( /dev/null
) apenas para garantir que esses *.txt
itens sejam sempre tratados como arquivos de entrada, mesmo que contenham =
caracteres.
../foo
, /path/to/foo
e caminhos que estão em uma codificação diferente) - nesse caso substr(FILENAME,3)
, não será suficiente ou será um script de um tiro onde o usuário basicamente sabe o que os nomes de arquivos são - caso em que ele / ela provavelmente não deve se preocupar com qualquer um deles contendo =
quer ;-)
./
seja um problema, mas que pode ser indesejável sob certas condições, como casos em que o nome do arquivo deve ser incluído na saída, caso em que ./
deve ser redundante e desnecessário, para que você precisará se livrar dele de alguma forma. Aqui está pelo menos um exemplo . Quanto ao usuário saber o que são os nomes de arquivos - bem, neste caso, também sabemos o que é o nome do arquivo, mas =
ainda atrapalha o processamento adequado. Assim, a liderança pode -
atrapalhar.
./
prefixo para contornar esse awk
recurso (mis), mas então você acaba com um ./
resultado na saída que pode querer remover. Veja como verificar se a primeira linha do arquivo contém uma sequência específica? como um exemplo.
./
mas também o global (caminho absoluto) /
que faz o awk interpretar o argumento como um arquivo.
Na maioria das versões do awk, os argumentos após a execução do programa são:
x=y
Como seu nome de arquivo está sendo interpretado como caso 2, o awk ainda está esperando algo para ler no stdin (já que ele não percebe que houve qualquer nome de arquivo passado).
Portably, esse comportamento está documentado no POSIX :
Um dos dois tipos de argumento a seguir pode ser misturado:
- file: um nome de caminho de um arquivo que contém a entrada a ser lida, que corresponde ao conjunto de padrões no programa. Se nenhum operando de arquivo for especificado, ou se um operando de arquivo for '-', a entrada padrão deve ser usada.
- atribuição: um operando que começa com um caractere sublinhado ou alfabético do conjunto de caracteres portáteis (consulte a tabela no volume Definições básicas da IEEE Std 1003.1-2001, Seção 6.1, Conjunto de caracteres portáteis), seguido por uma sequência de sublinhados, dígitos, e os alfabéticos do conjunto de caracteres portáteis, seguidos pelo caractere '=', devem especificar uma atribuição de variável em vez de um nome de caminho.
Como tal, de maneira portável, você tem algumas opções (o primeiro é provavelmente o menos invasivo):
awk ... ./my=file
, que evita isso, pois .
não é "um caractere sublinhado ou alfabético do conjunto de caracteres portátil".awk ... < my=file
. No entanto, isso não funciona bem com vários arquivos.ln my=file my_file
e depois usar my_file
normalmente. Nenhuma cópia será executada e os dois arquivos serão apoiados pelos mesmos dados e metadados do inode. Depois de usá-lo, é seguro remover o link criado, pois o número de referências ao inode ainda será maior que 0../my=file
funciona? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Isso deve ser portátil, porque ./my
não é um nome de variável válido, portanto não deve ser analisado dessa maneira.
=
é precedido por um caractere sublinhado ou alfabético do conjunto de caracteres portáteis (consulte a tabela no volume Definições básicas da IEEE Std 1003.1-2001, Seção 6.1, Conjunto de caracteres portáteis), seguido por uma sequência de sublinhados, dígitos e alfabéticos do conjunto de caracteres portátil . portanto, um caminho de arquivo como ++foo=bar.txt
ou =foo
ou ./foo=bar
está tudo bem assim .
ou +
não [_a-zA-Z]
.
./my=file
será passado literalmente.
awk '{print $1,$2}' /etc/passwd
. O ponto é que ter o shell aberto o arquivo em oposição ao awk não faz diferença se o torna ou não procurável. Na verdade, awk '{exit}' < /etc/passwd
você esperaria awk
voltar ao final do primeiro registro exit
para ter certeza de que ele deixaria a posição dentro do padrão. O POSIX exige isso. /usr/xpg4/bin/awk
faz isso no Solaris, mas gawk
nem mawk
parece fazê-lo no GNU / Linux.
awk
esse caminho.
Para citar a documentação do gawk (observe a ênfase adicionada):
Quaisquer argumentos adicionais na linha de comandos são normalmente tratados como arquivos de entrada a serem processados na ordem especificada. No entanto, um argumento que tem o formato var = value, atribui o valor do valor à variável var - ele não especifica um arquivo.
Por que o comando para e espera? Como no formulário awk 'processing_script_here' my=file.txt
não há arquivo especificado pela definição acima - my=file.txt
é interpretado como atribuição de variável, e se não houver arquivo definido, awk
será lido stdin (também é evidente a partir do strace
qual mostra que o awk nesse comando está aguardando read(0,'...)
syscall.
Isso também está documentado nas especificações do POSIX awk , consulte a seção OPERANDS e parte das atribuições )
A atribuição de variável é evidente, awk '{print foo}' foo=bar /etc/passwd
pois o valor de foo
é impresso para cada linha em / etc / passwd. ./foo=bar
No entanto, especificar ou caminho completo funciona.
Note que a execução strace
em awk '1' foo=bar
, bem como verificar com cat foo=bar
mostra que este é problema específico do awk, e execve faz show de nome de arquivo como argumento passado, então conchas não têm nada a ver com atribuições de variáveis env neste caso.
Além disso, observe que awk '...script...' foo=bar
isso não causará a criação de variáveis de ambiente pelo shell, pois as atribuições de variáveis de ambiente devem estar precedendo um comando para entrar em vigor. Consulte Regras de gramática do POSIX Shell , ponto número 7. Além disso, isso pode ser verificado viaawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd