shell - Cómo usar argumentos múltiples para awk con un shebang(es decir,#!)?
unix gawk (9)
Me gustaría ejecutar un script gawk con --re-interval
usando un shebang. El enfoque "ingenuo" de
#!/usr/bin/gawk --re-interval -f
... awk script goes here
no funciona, ya que se llama a gawk con el primer argumento "--re-interval -f"
(no dividido alrededor del espacio en blanco), que no comprende. ¿Hay alguna solución para eso?
Por supuesto, no puedes llamar a gawk directamente, sino envolverlo en un script de shell que divide el primer argumento, o crear un script de shell que luego llame a gawk y poner el script en otro archivo, pero me preguntaba si había alguna forma de hacerlo esto dentro de un archivo.
El comportamiento de las líneas shebang difiere de un sistema a otro, al menos en Cygwin no divide los argumentos por espacios en blanco. Me importa cómo hacerlo en un sistema que se comporta de esa manera; el guion no está destinado a ser portátil.
¿Por qué no usar bash
y gawk
, pasar el shebang, leer el script y pasarlo como un archivo a una segunda instancia de 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
}
(-lo mismo podría lograrse naturalmente con, por ejemplo, sed
o tail
, pero creo que hay algún tipo de belleza que depende solo de bash
y gawk
;)
En Cygwin y Linux, todo después de que la ruta del shebang se analice en el programa como un argumento.
Es posible piratear esto usando otra secuencia de comandos awk
dentro del shebang:
#!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}
Esto ejecutará {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}
{system("/usr/bin/gawk --re-interval -f " FILENAME); exit}
en awk.
Y esto ejecutará /usr/bin/gawk --re-interval -f path/to/your/script.awk
en el shell de su sistema.
En el manual de gawk (http://www.gnu.org/manual/gawk/gawk.html), al final de la sección 1.14 se observa que solo se debe usar un único argumento cuando se ejecuta gawk desde una línea de shebang. Dice que el sistema operativo tratará todo después del camino para mirar como un único argumento. Quizás haya otra forma de especificar la opción --re-interval
? Tal vez su secuencia de comandos puede hacer referencia a su caparazón en la línea shebang, ejecutar gawk
como un comando e incluir el texto de su secuencia de comandos como un "documento aquí".
Esto parece funcionar para mí con (g) awk.
#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"
# The real awk program starts here
{ print $0 }
Tenga en cuenta el #!
ejecuta /bin/sh
, por lo que este script se interpreta primero como un script de shell.
Al principio, simplemente probé "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"
, pero awk lo trató como un comando e imprimió cada línea de entrada incondicionalmente Es por eso que puse el arbitrary_long_name==0
- se supone que falla todo el tiempo. Podrías reemplazarlo con una cuerda de galimatías. Básicamente, estaba buscando una condición falsa en awk que no afectara negativamente al script de shell.
En el script de shell, arbitrary_long_name==0
define una variable llamada arbitrary_long_name
y la establece igual a =0
.
La línea shebang nunca se ha especificado como parte de POSIX, SUS, LSB o cualquier otra especificación. AFAIK, ni siquiera ha sido debidamente documentado.
Existe un consenso aproximado sobre lo que hace: ¡tomar todo entre el !
y /n
exec
it. ¡La suposición es que todo entre el !
y /n
es una ruta absoluta completa al intérprete. No hay consenso sobre qué sucede si contiene espacios en blanco.
- Algunos sistemas operativos simplemente tratan todo como el camino. Después de todo, en la mayoría de los sistemas operativos, el espacio en blanco o los guiones son legales en una ruta.
- Algunos sistemas operativos se dividen en espacios en blanco y tratan la primera parte como la ruta al intérprete y el resto como argumentos individuales.
- Algunos sistemas operativos se dividen en el primer espacio en blanco y tratan la parte frontal como la ruta al interperador y el resto como un único argumento (que es lo que está viendo).
- Algunos incluso no admiten líneas shebang en absoluto .
Afortunadamente, 1. y 4. parecen haberse extinguido, pero 3. está bastante extendido, por lo que simplemente no puede confiar en poder aprobar más de un argumento.
Y dado que la ubicación de los comandos tampoco está especificada en POSIX o SUS, generalmente utiliza ese único argumento pasando el nombre del ejecutable a env
para que pueda determinar la ubicación del ejecutable; p.ej:
#!/usr/bin/env gawk
[Obviamente, esto todavía asume una ruta particular para env
, pero solo hay muy pocos sistemas donde viva en /bin
, por lo que esto generalmente es seguro. La ubicación de env
es mucho más estandarizada que la ubicación de gawk
o incluso algo peor como python
o ruby
o spidermonkey
.]
Lo que significa que no puedes usar ningún argumento en absoluto .
Me encontré con el mismo problema, sin una solución aparente debido a la forma en que los espacios en blanco se tratan en un shebang (al menos en Linux).
Sin embargo, puede pasar varias opciones en un shebang, siempre que sean opciones cortas y puedan concatenarse (el modo GNU).
Por ejemplo, no puedes tener
#!/usr/bin/foo -i -f
pero puedes tener
#!/usr/bin/foo -if
Obviamente, eso solo funciona cuando las opciones tienen equivalentes cortos y no toman argumentos.
Para una solución portátil, use awk
lugar de gawk
, invoque el shell BOURNE estándar ( /bin/sh
) con su shebang, e invoque awk
directamente, pasando el programa en la línea de comando como un documento aquí en lugar de vía stdin:
#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF
Nota: no -f
argumento para awk
. Eso deja stdin
disponible para que awk
lea la entrada de. Asumiendo que tienes gawk
instalado y en tu PATH
, eso logra todo lo que creo que intentabas hacer con tu ejemplo original (suponiendo que quisieras que el contenido del archivo fuera el script awk y no el input, que creo que tu enfoque de shebang hubiera tratado como).
Solo por diversión: existe la siguiente solución bastante extraña que redirecciona stdin y el programa a través de los descriptores de archivos 3 y 4. También puede crear un archivo temporal para el 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
Una cosa es molesta al respecto: el shell realiza una expansión variable en el script, por lo que debe citar cada $ (como se hizo en la segunda línea del script) y probablemente más que eso.
#!/bin/sh
'''''':''
exec YourProg -some_options "$0" "$@"
''''''
El truco shebang de shell anterior es más portable que /usr/bin/env
.