Como imprimir o próprio nome do script no mawk?

13

No bash $0contém o nome do script, mas no awk se eu criar um script chamado myscript.awk com o seguinte conteúdo:

#!/usr/bin/awk -f
BEGIN{ print ARGV[0] }

e execute, ele imprimirá apenas "awk". Além disso, o ARGV [i] com i> 0 é usado apenas para argumentos de script na linha de comando. Então, como fazê-lo imprimir o nome do script, neste caso "myscript.awk"?

cipper
fonte
Eu mudei o título de awk para mawk porque todas as soluções exigem gawk e não funcionam com awk geral, e em particular com mawk que é amplamente utilizado (por exemplo, padrão no Ubuntu)
cipper
O que faz você pensar que mawké o padrão no Ubuntu? Na minha VM 15.04, o padrão awké gawk. Enquanto o mawk está instalado, não é o padrão.
terdon
1
É um script awk, se você o chamar awk -f myscript.awk. No entanto, isso não tem relação com o problema em questão.
cipper
1
@ EdMorton É um awkscript porque começa com #!/usr/bin/awk -f. Os scripts do shell começam com #!/bin/sh(ou algo semelhante).
Barmar
1
Estive conversando com vários especialistas em shell e tentando obter uma resposta definitiva sobre se é um script shell ou awk e, surpreendentemente, de acordo com o POSIX, a interpretação dos arquivos que começam com #! é indefinido e não possui um nome de tipo específico. Enquanto algumas pessoas se referem a ele como um "script interpretador de hash bang" em vez de um script shell ou awk, o consenso parece ser que ele deve ser considerado um script awk mesmo que o kernel (não shell) interprete a primeira linha porque o awk ainda também precisa analisar a primeira linha (como um comentário) e você pode executá-la usando awk -f file.
Ed Morton

Respostas:

5

Com o GNU awk 4.1.3 no bash no cygwin:

$ cat tst.sh
#!/bin/awk -f
BEGIN { print "Executing:", ENVIRON["_"] }

$ ./tst.sh
Executing: ./tst.sh

Não sei como isso é portátil. Como sempre, porém, eu não executaria um script awk usando um shebang em um script de shell, pois apenas rouba a funcionalidade possível. Mantenha-o simples e faça isso:

$ cat tst2.sh
awk -v cmd="$0" '
BEGIN { print "Executing:", cmd }
' "$@"

$ ./tst2.sh
Executing: ./tst2.sh

Esse último funcionará com qualquer awk moderno em qualquer shell em qualquer plataforma.

Ed Morton
fonte
Observe que o primeiro funciona apenas em bash, zsh ou ksh. O último é sobre o shell script, não o script awk.
cuonglm
2
Obrigado! ENVIRON["_"]funciona perfeitamente e não chama nenhum programa externo. A segunda opção awk -v ...depende de como se executa o script; Eu não quero isso
cipper
1
Chamar seu script tst.shé enganador. É um awkscript, não um script de shell. BEGINnão é um comando shell válido.
Barmar
1
Certo, mas a questão da portabilidade não é "O ENVIRON [] é portátil" é " ENVIRON["_"]produz o caminho do script de shell de chamada quando impresso a partir de cada awk chamado via shebang de cada shell"? Eu nunca chamaria um script awk de um shebang para eu pessoalmente não me importo com a resposta, mas pensei em mencioná-la .... Ah, eu vejo nos comentários acima que @cuonglm respondeu que isso é suportado apenas em algumas conchas .
Ed Morton
1
Bom ponto, @Ed. Verificado como com falha no traço (que retorna o comando anterior (ou o próprio shell) em vez do atual). O ksh93 prefixa interessante o PID em asteriscos, por exemplo *12345*/tmp/test.awk. ARGV[0]está sempre confiável awkem dash, bash, zsh e ksh93.
Adam Katz #
5

Eu não acho que isso é possível conforme a gawk documentação :

Finalmente, o valor de ARGV[0](consulte a seção 7.5 Variáveis ​​internas) varia de acordo com o seu sistema operacional. Alguns sistemas colocam awklá, outros colocam o nome completo do caminho do awk (como /bin/awk), e outros colocam o nome do seu script ('conselhos'). Não confie no valor de ARGV[0]para fornecer seu nome de script.

Em linuxvocê pode tentar usar uma espécie de um truque sujo e como apontado nos comentários por Stéphane Chazelas é possível se a implementação de awksuportes NUL bytes:

#!/usr/bin/awk -f

BEGIN { getline t < "/proc/self/cmdline"; split(t, a, "\0"); print a[3]; }
taliezin
fonte
seu script como está parece não estar funcionando. Ele só imprime "k" Se chamado com "awk -f script.awk", e imprime "s" se chamado por "./script.awk"
cipper
@ipper: Aqui ele trabalha gawke falha (como sua descrição) mawk. Interessante!
Funciona para mim no linux, awk- 4.0.2. No freebsd com /proc/curpoc/cmdline, e o awkresultado é como o seu, mas funciona com gawk.
taliezin
No ubuntu padrão, ele não funciona. Seria bom encontrar uma solução portátil.
cipper
1
@taliezin: a resposta de cuonglm não é uma solução, pois requer alimentar manualmente o script com seu nome. É como ligar awk -vNAME="myscript.awk" ./myscript.awke imprimir NAME dentro do script. Não é uma solução.
cipper
5

Não conheço nenhuma maneira direta de obter o nome do comando no awk. No entanto, você pode encontrá-lo através de um sub-shell.

gawk

Com o GNU awk e o pscomando, você pode usar o ID do processo PROCINFO["PID"]para recuperar o nome do comando como uma solução alternativa. Por exemplo:

cmdname.awk

#!/usr/bin/gawk -f

BEGIN {
  ("ps -p " PROCINFO["pid"] " -o comm=") | getline CMDNAME
  print CMDNAME
}

mawk e nawk

Você pode usar a mesma abordagem, mas derivar awko PID da $PPIDvariável especial do shell (PID do pai):

cmdname.awk

#!/usr/bin/mawk -f

BEGIN { 
  ("ps -p $PPID -o comm=") | getline CMDNAME
  print CMDNAME
}

Teste

Execute o script assim:

./cmdname.awk

Saída nos dois casos:

cmdname.awk
Thor
fonte
Eu recebi um erro: / bin / sh: 1: -o: not found
cipper
@ipper: Isso só funciona com o GNU awk, adicionei a linha shebang ausente.
Thor
Do manual gawk : De acordo com o POSIX, 'expression | getline 'é ambíguo se a expressão contiver operadores não parênteses, além de' $ '- por exemplo,' "echo" "date" | getline 'é ambíguo porque o operador de concatenação não está entre parênteses. Você deve escrevê-lo como '("eco" "data") | getline 'se você quiser que seu programa seja portátil para todas as implementações do awk.
cipper
1
Se necessário gawk, é uma gawksolução em vez de uma awksolução. Eu acho que o @cipper deve adicionar seu desejo "uma solução portátil" à pergunta.
1
@ Thor: a resposta da cuonglm não é uma solução, pois requer alimentar manualmente o script com seu nome. É como ligar awk -vNAME="myscript.awk" ./myscript.awke imprimir NAME dentro do script. Não é uma solução.
cipper
4

Com o POSIX awk:

#!/usr/bin/awk -f

BEGIN {
    print ENVIRON["AWKSCRIPT"]
}

Então:

AWKSCRIPT=test.awk ./test.awk
test.awk
cuonglm
fonte
4
Você alimentar manualmente o nome do script nele, isso não é uma forma de auto-impressão
cipper
@ipper: Bem, essa é a maneira mais fácil e portátil que posso imaginar.
cuonglm
2
É como chamar awk -vNAME="myscript.awk" ./myscript.awke depois imprimir a variável NAMEdentro do script. Não é uma solução.
cipper
@ipper: Essa é a única maneira, se você mencionar mawk. E também usar ENVIRONnão é o mesmo que usar -vNAME="myscript.awk", pois quando mawkexpandirá a sequência de escape NAME.
cuonglm 8/09/15
4

Usando o GNU awk

Verificando o guia do usuário do GNU awk - 7.5.2 Variáveis ​​internas que transmitem informações Eu me deparei com:

PROCINFO #

Os elementos dessa matriz fornecem acesso a informações sobre o programa awk em execução. Os seguintes elementos (listados em ordem alfabética) estão garantidos como disponíveis:

PROCINFO ["pid"]

O ID do processo atual.

Isso significa que você pode conhecer o PID do programa durante o tempo de execução. Então, é uma questão de usar system()para procurar o processo com este PID fornecido:

#!/usr/bin/gawk -f
BEGIN{ pid=PROCINFO["pid"]
       system("ps -ef | awk '$2==" pid " {print $NF}'")
}

Eu uso ps -ef, que exibe o PID na 2ª coluna. Assumindo que a execução é feita através awk -f <script>e sem outros parâmetros, podemos assumir que o último campo da linha contém as informações que queremos.

Caso tivéssemos alguns parâmetros, teríamos que analisar a linha de maneira diferente - ou melhor, usar algumas das opções de pspara imprimir apenas as colunas nas quais estamos interessados.

Teste

$ awk -f a.awk 
a.awk
$ cp a.awk hello.awk
$ awk -f hello.awk 
hello.awk

Observe também que outro capítulo do guia do usuário do GNU awk nos diz que o ARGV não é o caminho a seguir:

1.1.4 Programas executáveis ​​do awk

Finalmente, o valor de ARGV [0] (consulte Variáveis ​​internas) varia de acordo com o seu sistema operacional. Alguns sistemas colocam 'awk' lá, outros colocam o nome completo do caminho do awk (como / bin / awk) e outros colocam o nome do seu script ('conselhos'). (dc) Não confie no valor de ARGV [0] para fornecer seu nome de script.

fedorqui
fonte
infelizmente o PROCINFO é apenas um recurso gawk, não um awk geral. Por exemplo, ele não está disponível no mawk (que é instalado por padrão no ubuntu)
cipper
Eu sei ... Por que você marcou a pergunta com [gawk] então?
fedorqui 8/09/2015
Você está certo. Quando postei a pergunta, não estava ciente de todas essas diferenças entre mawk e gawk. A tag mudou para mawk agora.
cipper
@cipper good:) Na verdade, eu estava testando mawke não conseguia fazê-lo funcionar, de modo que instalei gawkno meu Ubuntu e funcionou. Portanto, uma solução alternativa pode ser usar gawk: D
fedorqui
1
@terdon, gawknão está instalado por padrão no Ubuntu (ou pelo menos em algumas versões do Ubuntu, onde mawkestá a awkimplementação padrão ). IIRC, eu tive que instalá-lo também no Debian.
Stéphane Chazelas