Como usar vários argumentos para o awk com um shebang (ou seja, #!)?

Eu gostaria de executar um script --re-interval com --re-interval usando um shebang. A abordagem “ingênua” de

 #!/usr/bin/gawk --re-interval -f ... awk script goes here 

não funciona, já que o gawk é chamado com o primeiro argumento "--re-interval -f" (não dividido ao redor do espaço em branco), que ele não entende. Existe uma solução para isso?

É claro que você pode não chamar o gawk diretamente, mas colocá-lo em um shell script que divide o primeiro argumento, ou fazer um script que chame o gawk e coloque o script em outro arquivo, mas eu queria saber se havia alguma maneira de fazer isso. isso dentro de um arquivo.

O comportamento das linhas shebang difere de sistema para sistema – pelo menos no Cygwin ele não divide os argumentos por espaços em branco. Eu apenas me importo sobre como fazer isso em um sistema que se comporta assim; o roteiro não é para ser portátil.

Isso parece funcionar para mim com (g) awk.

 #!/bin/sh arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@" # The real awk program starts here { print $0 } 

Observe o #! executa /bin/sh , portanto, esse script é interpretado primeiro como um script de shell.

No começo, eu simplesmente tentei "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@" , mas o awk tratou isso como um comando e imprimiu todas as linhas de input incondicionalmente. É por isso que coloquei o arbitrary_long_name==0 – ele deve falhar o tempo todo. Você poderia substituí-lo com alguma string sem sentido. Basicamente, eu estava procurando por uma condição falsa no awk que não afetaria negativamente o shell script.

No shell script, o arbitrary_long_name==0 define uma variável chamada arbitrary_long_name e define igual a =0 .

A linha shebang nunca foi especificada como parte de POSIX, SUS, LSB ou qualquer outra especificação. AFAIK, nem foi devidamente documentado.

Há um consenso aproximado sobre o que faz: levar tudo entre os ! e o \n e exec . A suposição é que tudo entre o ! e o \n é um caminho absoluto completo para o interpretador. Não há consenso sobre o que acontece se contiver espaço em branco.

  1. Alguns sistemas operacionais simplesmente tratam a coisa toda como o caminho. Afinal, na maioria dos sistemas operacionais, espaços em branco ou traços são legais em um caminho.
  2. Alguns sistemas operacionais se dividem em espaços em branco e tratam a primeira parte como o caminho para o interpretador e o restante como argumentos individuais.
  3. Alguns sistemas operacionais se dividem no primeiro espaço em branco e tratam a parte frontal como o caminho para o interceptador e o restante como um único argumento (que é o que você está vendo).
  4. Alguns até não apoiam linhas de shebang em absoluto .

Felizmente, 1. e 4. parecem ter morrido, mas 3. é bastante difundido, então você simplesmente não pode confiar em ser capaz de passar mais de um argumento.

E como a localização de comandos também não é especificada em POSIX ou SUS, você geralmente usa esse único argumento passando o nome do executável para env para que ele possa determinar a localização do executável; por exemplo:

 #!/usr/bin/env gawk 

[Obviamente, isto ainda assume um caminho particular para env , mas existem poucos sistemas onde ele mora em /bin , então isso geralmente é seguro. A localização do env é muito mais padronizada do que a localização do gawk ou algo pior, como python ruby ou spidermonkey .]

O que significa que você não pode realmente usar argumentos.

Eu me deparei com o mesmo problema, sem solução aparente por causa da forma como os espaços em branco são tratados em um shebang (pelo menos no Linux).

No entanto, você pode passar várias opções em um shebang, desde que sejam opções curtas e possam ser concatenadas (a maneira GNU).

Por exemplo, você não pode ter

 #!/usr/bin/foo -i -f 

mas você pode ter

 #!/usr/bin/foo -if 

Obviamente, isso só funciona quando as opções têm equivalentes curtos e não aceitam argumentos.

No Cygwin e no Linux, tudo após o caminho do shebang é analisado pelo programa como um argumento.

É possível hackear isso usando outro script awk dentro da shebang:

 #!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit} 

Isto irá executar {system("/usr/bin/gawk --re-interval -f " FILENAME); exit} {system("/usr/bin/gawk --re-interval -f " FILENAME); exit} no awk.
E isto irá executar /usr/bin/gawk --re-interval -f path/to/your/script.awk no seu shell de sistemas.

 #!/bin/sh ''':' exec YourProg -some_options "$0" "$@" ''' 

O truque shebang do shell acima é mais portátil que o /usr/bin/env .

No manual do gawk (http://www.gnu.org/manual/gawk/gawk.html), o final da seção 1.14 nota que você deve usar apenas um único argumento ao executar o gawk a partir de uma linha shebang. Ele diz que o sistema operacional irá tratar tudo após o caminho para se embasbacar como um único argumento. Talvez haja outra maneira de especificar a opção --re-interval ? Talvez seu script possa referenciar seu shell na linha shebang, executar o gawk como um comando e include o texto do seu script como um “documento aqui”.

Por que não usar o bash e o próprio gawk para pular pastilhas, ler o script e passá-lo como um arquivo para uma segunda instância do gawk [--with-whatever-number-of-params-you-need] ?

 #!/bin/bash gawk --re-interval -f < (gawk 'NR>3' $0 ) exit { print "Program body goes here" print $1 } 

(-O mesmo também poderia ser feito naturalmente com, por exemplo, sed ou tail , mas eu acho que há algum tipo de beleza dependendo apenas do bash e do próprio gawk 😉

Apenas por diversão: há a seguinte solução bastante estranha que redireciona o stdin eo programa através dos descritores de arquivo 3 e 4. Você também pode criar um arquivo temporário para o script.

 #!/bin/bash exec 3>&0 exec < <-EOF 4>&0 BEGIN {print "HALLO"} {print \$1} EOF gawk --re-interval -f < (cat 0>&4) 0>&3 

Uma coisa é chata: o shell faz uma expansão de variables ​​no script, então você tem que citar cada $ (como feito na segunda linha do script) e provavelmente mais do que isso.

Para uma solução portátil, use awk vez de gawk , invoque o shell BOURNE padrão ( /bin/sh ) com seu shebang e invoque diretamente o awk , passando o programa na linha de comando como um documento aqui em vez de via stdin:

 #!/bin/sh gawk --re-interval < < 

Nota: nenhum argumento -f para awk . Isso deixa o stdin disponível para o awk ler a input de. Supondo que você tenha o gawk instalado e em seu PATH , ele obtém tudo o que eu acho que você estava tentando fazer com o seu exemplo original (supondo que você queria que o conteúdo do arquivo fosse o script awk e não a input, que eu acho que como).