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=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.
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/ RSpor. 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 file1estiver 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 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:
awk -f file.awk -vfoo=bar.txt
(aplica-se também para awk '{code}' -vfoo=bar.txtcom o awkde versões busybox antes 1.28.0, ver relatório de erros correspondente ).
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.
../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 ;-)
./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 awkrecurso (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=yComo 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_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../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.
=é 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].
./my=fileserá 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/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.
awkesse 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, 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