¿Cómo imprimir el nombre del script propio en mawk?

13

En bash $0contiene el nombre del script, pero en awk si hago un script llamado myscript.awk con el siguiente contenido:

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

y ejecutarlo, solo imprimirá "awk". Además, ARGV [i] con i> 0 se usa solo para argumentos de script en la línea de comandos. Entonces, ¿cómo hacer que imprima el nombre del script, en este caso "myscript.awk"?

cortador
fuente
He cambiado el título de awk a mawk porque todas las soluciones requieren gawk y no funcionan con awk general, y en particular con mawk que es ampliamente utilizado (por ejemplo, predeterminado en Ubuntu)
cipper
¿Qué te hace pensar que mawkes predeterminado en Ubuntu? En mi VM 15.04, el valor predeterminado awkes gawk. Mientras mawk está instalado, no es el predeterminado.
terdon
1
Es un guión awk si lo llamas awk -f myscript.awk. Sin embargo, esto no está relacionado con el problema en cuestión.
cipper
1
@EdMorton Es un awkscript porque comienza con #!/usr/bin/awk -f. Los scripts de shell comienzan con #!/bin/sh(o algo similar).
Barmar
1
He estado hablando con varios expertos en shell e intentando obtener una respuesta definitiva sobre si se trata de un script shell o awk y, sorprendentemente, según POSIX, la interpretación de los archivos que comienzan con #. no está definido y no tiene un nombre de tipo específico. Si bien algunas personas se refieren a él como un "guión de intérprete de hash bang" en lugar de un guión shell o awk, el consenso parece ser que debería considerarse un guión awk a pesar de que el núcleo (no el shell) interpreta la primera línea porque awk todavía tiene que poder analizar esa primera línea también (como comentario) y puede ejecutarla usando awk -f file.
Ed Morton

Respuestas:

5

Con GNU awk 4.1.3 en bash en cygwin:

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

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

No sé cuán portátil es eso. Sin embargo, como siempre, no ejecutaría un script awk usando un shebang en un script de shell, ya que simplemente le roba posibles funcionalidades. Manténgalo simple y simplemente haga esto en su lugar:

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

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

Eso último funcionará con cualquier awk moderno en cualquier shell en cualquier plataforma.

Ed Morton
fuente
Tenga en cuenta que el primero solo funciona en bash, zsh o ksh. El último trata sobre el script de shell, no el script awk.
Cuonglm
2
¡Gracias! ENVIRON["_"]funciona perfectamente y no llama a ningún programa externo. La segunda opción awk -v ...depende de cómo se ejecuta el script; No quiero esto
cipper
1
Llamar a su guión tst.shes engañoso. Es un awkscript, no un script de shell. BEGINNo es un comando de shell válido.
Barmar
1
Correcto, pero la pregunta de portabilidad no es "¿es ENVIRON [] portátil" es " ENVIRON["_"]produce la ruta del script del shell de llamada cuando se imprime desde cada awk llamado a través de un shebang desde cada shell"? Nunca llamaría un guión awk de un shebang a mí personalmente no me importa la respuesta, pero pensé en mencionarla ... Oh, veo en los comentarios anteriores que @cuonglm respondió que solo es compatible con algunos shells .
Ed Morton
1
Buen punto, @Ed. Verificado como error en el guión (que devuelve el comando anterior (o el shell en sí) en lugar del actual). Curiosamente, ksh93 prefija el PID en asteriscos, por ejemplo *12345*/tmp/test.awk. ARGV[0]siempre es confiable awken dash, bash, zsh y ksh93.
Adam Katz
5

No creo que esto sea posible según la gawk documentación :

Finalmente, el valor de ARGV[0](consulte la sección 7.5 Variables incorporadas) varía según su sistema operativo. Algunos sistemas se colocan awkallí, algunos ponen el nombre de ruta completo de awk (como /bin/awk), y algunos ponen el nombre de su script ('consejo'). No confíe en el valor de ARGV[0]proporcionar su nombre de script.

En linuxpuede intentar usar una especie de truco sucio y, como lo señalaron los comentarios de Stéphane Chazelas , es posible si la implementación de awkNUL admite bytes:

#!/usr/bin/awk -f

BEGIN { getline t < "/proc/self/cmdline"; split(t, a, "\0"); print a[3]; }
taliezin
fuente
su script tal como está parece no funcionar. Simplemente imprime "k" si se llama con "awk -f script.awk", e imprime "s" si se llama por "./script.awk"
cipper
@cipper: Aquí funciona gawky falla (como su descripción) con mawk. ¡Interesante!
A mí me funciona en Linux, awk- 4.0.2. En freebsd con /proc/curpoc/cmdline, y el awkresultado es como el tuyo pero funciona con gawk.
taliezin
En ubuntu por defecto no funciona. Sería bueno encontrar una solución portátil.
cipper
1
@taliezin: la respuesta de cuonglm no es una solución, ya que requiere alimentar manualmente el script con su nombre. Es como llamar awk -vNAME="myscript.awk" ./myscript.awky luego imprimir NAME dentro del script. No es una solucion.
Cipper
5

No conozco ninguna forma directa de obtener el nombre del comando desde awk. Sin embargo, puede encontrarlo a través de un sub-shell.

papar moscas

Con GNU awk y el pscomando, puede usar la ID del proceso PROCINFO["PID"]para recuperar el nombre del comando como solución alternativa. Por ejemplo:

cmdname.awk

#!/usr/bin/gawk -f

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

mawk y nawk

Puede usar el mismo enfoque, pero derivar awkel PID de la $PPIDvariable de shell especial (PID del padre):

cmdname.awk

#!/usr/bin/mawk -f

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

Pruebas

Ejecute el script así:

./cmdname.awk

Salida en ambos casos:

cmdname.awk
Thor
fuente
Recibí un error: / bin / sh: 1: -o: no encontrado
cipper
@cipper: Esto solo funciona con GNU awk, agregué la línea shebang que falta.
Thor
Del manual de gawk : Según POSIX, 'expresión | getline 'es ambiguo si la expresión contiene operadores sin paréntesis que no sean' $ ', por ejemplo,' "echo" "date" | getline 'es ambiguo porque el operador de concatenación no está entre paréntesis. Debería escribirlo como '("echo" "date") | getline 'si desea que su programa sea portátil para todas las implementaciones de awk.
cipper
1
Si lo necesita gawk, es una gawksolución en lugar de una awksolución. Creo que @cipper debería agregar su deseo "una solución portátil" a la pregunta.
1
@Thor: la respuesta de cuonglm no es una solución, ya que requiere alimentar manualmente el script con su nombre. Es como llamar awk -vNAME="myscript.awk" ./myscript.awky luego imprimir NAME dentro del script. No es una solucion.
cipper
4

Con POSIX awk:

#!/usr/bin/awk -f

BEGIN {
    print ENVIRON["AWKSCRIPT"]
}

Luego:

AWKSCRIPT=test.awk ./test.awk
test.awk
Cuonglm
fuente
44
Usted alimenta manualmente el nombre de la secuencia de comandos en él, esta no es una forma de
autoimpresión
@cipper: Bueno, esa es la forma más fácil y portátil que puedo imaginar.
Cuonglm
2
Es como llamar awk -vNAME="myscript.awk" ./myscript.awky luego imprimir la variable NAMEdentro del script. No es una solucion.
Cipper
@cipper: Esa es la única forma, si lo mencionas mawk. Y también usar ENVIRONno es lo mismo que usar -vNAME="myscript.awk", ya que cuándo mawkexpandirá la secuencia de escape NAME.
Cuonglm
4

Usando GNU awk

Consultando la guía del usuario de GNU awk - 7.5.2 Variables incorporadas que transmiten información con la que me topé:

PROCINFO #

Los elementos de esta matriz proporcionan acceso a información sobre el programa awk en ejecución. Se garantiza que los siguientes elementos (enumerados alfabéticamente) estarán disponibles:

PROCINFO ["pid"]

El ID de proceso del proceso actual.

Esto significa que puede conocer el PID del programa durante el tiempo de ejecución. Entonces, es cuestión de usar system()para buscar el proceso con este PID dado:

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

Yo uso ps -ef, que muestra el PID en la segunda columna. Suponiendo que la ejecución se realiza awk -f <script>y no hay otros parámetros, podemos suponer que el último campo de la línea contiene la información que queremos.

En caso de que tuviéramos algunos parámetros, tendríamos que analizar la línea de manera diferente o, mejor, usar algunas de las opciones pspara imprimir solo las columnas que nos interesan.

Prueba

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

Tenga en cuenta también que otro capítulo de la guía del usuario de GNU awk nos dice que ARGV no es el camino a seguir:

1.1.4 Programas awk ejecutables

Finalmente, el valor de ARGV [0] (consulte Variables integradas) varía según su sistema operativo. Algunos sistemas ponen 'awk' allí, algunos ponen el nombre de ruta completo de awk (como / bin / awk), y algunos ponen el nombre de su script ('consejo'). (dc) No confíe en el valor de ARGV [0] para proporcionar su nombre de script.

fedorqui
fuente
desafortunadamente PROCINFO es solo una característica gawk, no general awk. Por ejemplo, no está disponible en mawk (que está instalado por defecto en ubuntu)
cipper
Lo sé ... ¿Por qué etiquetaste la pregunta con [gawk] entonces?
fedorqui
Tienes razón. Cuando publiqué la pregunta no estaba al tanto de todas estas diferencias entre mawk y gawk. La etiqueta ha cambiado a mawk ahora.
cipper
@cipper good:) Realmente estaba probando mawky no pude hacerlo funcionar, así que instalé gawken mi Ubuntu y funcionó. Entonces, una solución alternativa puede ser gawk: D
fedorqui
1
@terdon, gawkno está instalado por defecto en Ubuntu (o al menos algunas versiones de Ubuntu, donde mawkes la awkimplementación predeterminada ). IIRC, tuve que instalarlo también en Debian.
Stéphane Chazelas