Como Chris diz , os argumentos do formulário variablename=anythingsã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=valueexecutados antes das BEGINinstruções), em vez de nomes de arquivo 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 awknome 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/awkSolaris 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=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 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 txtarquivos não contenha =caracteres.
Além disso, lembre-se de que um argumento como esse -vfoo=bar.txtpode ser tratado como uma opção se você usar:
Novamente, o uso de ./*.txtsoluções para isso (o uso de um ./prefixo também ajuda com um arquivo chamado -que, de outra forma, awkentende como significando entrada padrão ).
É por isso também
#! /usr/bin/awk -f
shebangs realmente não funcionam. Enquanto esses var=valuepodem ser contornados, fixe os ARGVvalores (adicione um ./prefixo) em uma BEGINinstruçã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 awke não o awkscript.
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 awkcorrige todos esses problemas com sua -Eopção.
Depois -E, o gawk espera apenas o caminho do awkscript (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 ARGVlista em uma BEGINinstrução).
Você também pode usá-lo como:
gawk -e '...awk code here...'-E /dev/null *.txt
Usamos -Eum script vazio ( /dev/null) apenas para garantir que esses *.txtitens sejam sempre tratados como arquivos de entrada, mesmo que contenham =caracteres.
Não vejo como o caminho explícito que termina em FILENAME é um problema. O script awk é geral; nesse caso, ele deve lidar com todos os tipos de caminhos que terminam em FILENAME (incluindo, entre outros ../foo, /path/to/fooe 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 ;-)
mosvy
2
@mosvy Eu não acho que exista tanto que ./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.
Não é apenas o local (relativo a este diretório), ./mas também o global (caminho absoluto) /que faz o awk interpretar o argumento como um arquivo.
Isaac
21
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ário 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).
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):
Use awk ... ./my=file, que evita isso, pois .não é "um caractere sublinhado ou alfabético do conjunto de caracteres portátil".
Coloque o arquivo no 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_filee depois usar my_filenormalmente. 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.
Não ./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 ./mynão é um nome de variável válido, portanto não deve ser analisado dessa maneira.
Stephen Harris
2
Como diz o texto do POSIX, o problema ocorre apenas quando o primeiro =é 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.txtou =fooou ./foo=barestá tudo bem assim .ou +não [_a-zA-Z].
Stéphane Chazelas
11
O @SergiyKolodyazhnyy awk é externo ao shell, portanto, não importa qual você use. ./my=fileserá passado literalmente.
Chris Down
11
@SergiyKolodyazhnyy, mesmo para 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/passwdvocê esperaria awkvoltar ao final do primeiro registro exitpara ter certeza de que ele deixaria a posição dentro do padrão. O POSIX exige isso. /usr/xpg4/bin/awkfaz isso no Solaris, mas gawknem mawkparece fazê-lo no GNU / Linux.
Stéphane Chazelas
3
@mosvy, consulte a seção INPUT FILES em pubs.opengroup.org/onlinepubs/9699919799/utilities/… É útil em vários padrões de uso que só fazem sentido com arquivos regulares, como quando você deseja truncar um arquivo ou gravar dados nele em uma posição identificada por awkesse caminho.
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.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, awkserá lido stdin (também é evidente a partir do stracequal 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/passwdpois o valor de fooé impresso para cada linha em / etc / passwd. ./foo=barNo entanto, especificar ou caminho completo funciona.
Note que a execução straceem awk '1' foo=bar, bem como verificar com cat foo=barmostra 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=barisso 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
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 dasBEGIN
instruções), em vez de nomes de arquivo de entrada.Isso pode ser útil em coisas como:
Onde você pode especificar um arquivo diferente
FS
/RS
por. 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
=
é umawk
nome de variável válido .O que constitui um nome de variável válido em
awk
é mais rigoroso que emsh
.O POSIX exige 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 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 comoStéphane=Chazelas.txt
pode ou não, dependendo daawk
implementação e da localidade.Por isso, com o awk, é recomendável 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, lembre-se de que um argumento como esse
-vfoo=bar.txt
pode ser tratado como uma opção se você usar:(aplica-se também para
awk '{code}' -vfoo=bar.txt
com oawk
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
shebangs realmente não funcionam. Enquanto esses
var=value
podem ser contornados, fixe osARGV
valores (adicione um./
prefixo) em umaBEGIN
instrução:Isso não ajudará com as opções, pois elas são vistas
awk
e não oawk
script.Um problema cosmético em potencial com o uso desse
./
prefixo é que ele acabaFILENAME
, 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 doawk
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:
shebangs onde a lista de argumentos sempre são arquivos de entrada (observe que você ainda pode editar essa
ARGV
lista em umaBEGIN
instrução).Você também pode usá-lo como:
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.fonte
../foo
,/path/to/foo
e caminhos que estão em uma codificação diferente) - nesse casosubstr(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 esseawk
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 :
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 usarmy_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.fonte
./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ê esperariaawk
voltar ao final do primeiro registroexit
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, masgawk
nemmawk
parece fazê-lo no GNU / Linux.awk
esse caminho.Para citar a documentação do gawk (observe a ênfase adicionada):
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 dostrace
qual mostra que o awk nesse comando está aguardandoread(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 defoo
é impresso para cada linha em / etc / passwd../foo=bar
No entanto, especificar ou caminho completo funciona.Note que a execução
strace
emawk '1' foo=bar
, bem como verificar comcat 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
fonte