Na maioria das versões do awk, os argumentos após a execução do programa são:
Um arquivo
Uma atribuição do formuláriox=y
Como seu nome de arquivo está sendo interpretado como caso #2, o awk ainda está esperando que algo seja lido em stdin (já que ele não percebe que algum nome de arquivo foi passado).
Qualquer 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 sublinhado ou caractere alfabético do conjunto de caracteres portátil (consulte a tabela no volume Definições de Base do 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ética do conjunto de caracteres portátil, seguido pelo caractere '=', deve especificar uma atribuição de variável em vez de um nome de caminho.
Como tal, portátilmente, você tem algumas opções (o nº 1 é provavelmente o menos intrusivo):
Use awk ... ./my=file, que evita isso, pois .não é "um sublinhado ou caractere alfabético do conjunto de caracteres portátil".
Coloque o arquivo em stdin usando awk ... < my=file. No entanto, isso não funciona bem com vários arquivos.
Faça um hardlink para o arquivo temporariamente e use-o. Você pode fazer algo como ln my=file my_file, e depois usar my_filenormalmente. Nenhuma cópia será executada e ambos os 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.
Como diz Chris , os argumentos do formulário variablename=anythingsão tratados como atribuição de variáveis (que são executadas no momento em que os argumentos são processados em oposição aos (mais recentes) -v var=valueque são executados antes das BEGINinstruções) em vez de nomes de arquivos de entrada.
Mas isso atrapalha quando você tem arquivos cujo nome contém =caracteres.
Agora, isso é apenas um problema quando o que resta do primeiro é um nome de variável =válido .awk
O que constitui um nome de variável válido em awké mais estrito do que em sh.
POSIX requer 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/awkSolaris 11, pelo menos, não é compatível a esse respeito e permite quaisquer caracteres alfabéticos na localidade em nomes de variáveis, não apenas a-zA-Z.
Portanto, um argumento como x+y=fooou =barou ./foo=barainda é 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.txtpode ou não, dependendo da awkimplementação e localidade.
É por isso que com awk, é recomendado 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 txtarquivos não contenha =caracteres.
Além disso, esteja ciente de que um argumento como -vfoo=bar.txtpode ser tratado como uma opção se você usar:
awk -f file.awk -vfoo=bar.txt
(também se aplica às awk '{code}' -vfoo=bar.txtversões awkdo busybox anteriores a 1.28.0, veja o relatório de bug correspondente ).
Novamente, o uso ./*.txtcontorna isso (usar um ./prefixo também ajuda com um arquivo chamado -que, de outra forma awk, entende como entrada padrão ).
Por isso também
#! /usr/bin/awk -f
shebangs realmente não funcionam. Enquanto var=valueaqueles podem ser contornados fixando os ARGVvalores (adicione um ./prefixo) em uma BEGINdeclaraçã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 essas são vistas awke não o awkscript.
Um possível problema cosmético com o uso desse ./prefixo é que ele acaba em FILENAME, mas você sempre pode usá substr(FILENAME, 3)-lo para removê-lo se não quiser.
A implementação do GNU awkcorrige todos esses problemas com sua -Eopção.
Depois -Ede , o gawk espera apenas o caminho do awkscript (onde -ainda significa stdin) e, em seguida, uma lista apenas de caminhos de arquivos de entrada (e lá, nem -é tratado especialmente).
É especialmente projetado para:
#! /usr/bin/gawk -E
shebangs onde a lista de argumentos são sempre arquivos de entrada (observe que você ainda está livre para editar essa ARGVlista em uma BEGINinstrução).
Você também pode usá-lo como:
gawk -e '...awk code here...' -E /dev/null *.txt
Usamos -Ecom um script vazio ( /dev/null) apenas para garantir que *.txtdepois sejam sempre tratados como arquivos de entrada, mesmo que contenham =caracteres.
Quaisquer argumentos adicionais na linha de comando 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? Porque no formulário awk 'processing_script_here' my=file.txtnã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, lerá stdin (também evidente straceque mostra que awk em tal comando está aguardando read(0,'...)syscall.
Isso também está documentado nas especificações POSIX awk , veja a seção OPERANDS e parte das atribuições )
A atribuição de variável é evidente em awk '{print foo}' foo=bar /etc/passwdque o valor de fooé impresso para cada linha em /etc/passwd. Especificar ./foo=barou caminho completo, no entanto, funciona.
Observe que stracea execução awk '1' foo=bar, bem como a verificação com cat foo=bar, mostra que esse é um problema específico do awk, e o execve mostra o nome do arquivo como argumento passado, portanto, os shells não têm nada a ver com as atribuições de variáveis env neste caso.
Além disso, observe que awk '...script...' foo=barisso não causará a criação de variáveis de ambiente pelo shell, pois as atribuições de variáveis de ambiente devem preceder um comando para entrar em vigor. Veja POSIX Shell Grammar Rules , ponto número 7. Além disso, isso pode ser verificado viaawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd
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 que algo seja lido em stdin (já que ele não percebe que algum nome de arquivo foi passado).
Portátil, esse comportamento está documentado em POSIX :
Como tal, portátilmente, você tem algumas opções (o nº 1 é provavelmente o menos intrusivo):
awk ... ./my=file
, que evita isso, pois.
não é "um sublinhado ou caractere 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 usarmy_file
normalmente. Nenhuma cópia será executada e ambos os 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.Como diz Chris , os argumentos do formulário
variablename=anything
são tratados como atribuição de variáveis (que são executadas no momento em que os argumentos são processados em oposição aos (mais recentes)-v var=value
que são executados antes dasBEGIN
instruções) em vez de nomes de arquivos de entrada.Isso pode ser útil em coisas como:
Onde você pode especificar um
FS
/RS
por arquivo diferente. Também é comumente usado em:Qual é uma versão mais segura de:
(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 nome de variável
=
válido .awk
O que constitui um nome de variável válido em
awk
é mais estrito do que emsh
.POSIX requer que seja algo como:
Com apenas caracteres do conjunto de caracteres portátil. No entanto, o
/usr/xpg4/bin/awk
Solaris 11, pelo menos, não é compatível a esse respeito e permite quaisquer caracteres alfabéticos na localidade em 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 comoStéphane=Chazelas.txt
pode ou não, dependendo daawk
implementação e localidade.É por isso que com awk, é recomendado usar:
ao invés de
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, esteja ciente de que um argumento como
-vfoo=bar.txt
pode ser tratado como uma opção se você usar:(também se aplica às
awk '{code}' -vfoo=bar.txt
versõesawk
do busybox anteriores a 1.28.0, veja o relatório de bug correspondente ).Novamente, o uso
./*.txt
contorna isso (usar um./
prefixo também ajuda com um arquivo chamado-
que, de outra formaawk
, entende como entrada padrão ).Por isso também
shebangs realmente não funcionam. Enquanto
var=value
aqueles podem ser contornados fixando osARGV
valores (adicione um./
prefixo) em umaBEGIN
declaração:Isso não ajudará com as opções, pois essas são vistas
awk
e não oawk
script.Um possível problema cosmético com o uso desse
./
prefixo é que ele acaba emFILENAME
, 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
de , o gawk espera apenas o caminho doawk
script (onde-
ainda significa stdin) e, em seguida, uma lista apenas de caminhos de arquivos de entrada (e lá, nem-
é tratado especialmente).É especialmente projetado para:
shebangs onde a lista de argumentos são sempre arquivos de entrada (observe que você ainda está livre para editar essa
ARGV
lista em umaBEGIN
instrução).Você também pode usá-lo como:
Usamos
-E
com um script vazio (/dev/null
) apenas para garantir que*.txt
depois sejam sempre tratados como arquivos de entrada, mesmo que contenham=
caracteres.Para citar a documentação do gawk (observe a ênfase adicionada):
Por que o comando para e espera? Porque 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 definidoawk
, lerá stdin (também evidentestrace
que mostra que awk em tal comando está aguardandoread(0,'...)
syscall.Isso também está documentado nas especificações POSIX awk , veja a seção OPERANDS e parte das atribuições )
A atribuição de variável é evidente em
awk '{print foo}' foo=bar /etc/passwd
que o valor defoo
é impresso para cada linha em /etc/passwd. Especificar./foo=bar
ou caminho completo, no entanto, funciona.Observe que
strace
a execuçãoawk '1' foo=bar
, bem como a verificação comcat foo=bar
, mostra que esse é um problema específico do awk, e o execve mostra o nome do arquivo como argumento passado, portanto, os shells não têm nada a ver com as 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 preceder um comando para entrar em vigor. Veja POSIX Shell Grammar Rules , ponto número 7. Além disso, isso pode ser verificado viaawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd