¿Cómo usar múltiples argumentos para awk con un shebang (es decir, #!)?


118

Me gustaría ejecutar un script de gawk--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 entre los espacios en blanco), que no comprende. ¿Hay alguna solución para eso?

Por supuesto, no puede 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 coloque 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. Solo me importa cómo hacerlo en un sistema que se comporta así; el script no está destinado a ser portátil.


1
Un experimento tonto que acabo de hacer fue con un script usando otro script en la línea shebang, que dividió los argumentos correctamente.
Hasturkun

@Hasturkun, eso plantea otro problema, que el comportamiento de las líneas shebang también difiere de un sistema a otro, ya que si el programa invocado puede ser un script.
dubiousjim


Con las versiones recientes de gawk (> = 4.0), --re-intervalya no es necesario (ver [ gnu.org/software/gawk/manual/… ).

Respuestas:


25

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 las #!carreras/bin/sh , por lo que este script se interpreta primero como un script de shell.

Al principio, simplemente lo intenté "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@", pero awk lo trató como un comando e imprimió cada línea de entrada incondicionalmente. Por eso puse elarbitrary_long_name==0 - se supone que falla todo el tiempo. Podrías reemplazarlo con una cadena de galimatías. Básicamente, estaba buscando una condición falsa en awk que no afectaría negativamente al script de shell.

En el script de shell, arbitrary_long_name==0define una variable llamada arbitrary_long_namey la iguala a =0.


Esta es mi respuesta, pero me pregunto si es lo suficientemente portátil y robusto. ¿Depende específicamente de bash, o funcionará con cualquier POSIX sh? Y no lo uso a awkmenudo, así que no estoy seguro de que mi truco en la segunda línea sea una buena forma de forzar awka ignorar la línea.
Aaron McDaid

Justo lo que me estaba preguntando, +1, pero probablemente desaconsejable (de ahí los votos relativos).
Aaron Hall

¿Puedes explicar qué problemas podría tener esto, @AaronHall? Siempre que la variable arbitrary_long_nameno entre en conflicto con una variable utilizada en el programa awk real, no veo ningún problema. ¿Se me escapa algo?
Aaron McDaid

Úselo en #!/bin/sh -lugar de #!/bin/shpara proteger el script de posibles comportamientos incorrectos de una manera peligrosa si se invoca con un argumento cero que tiene -como primer carácter. Esto puede suceder accidentalmente en lenguajes de programación como C, donde es fácil equivocarse accidentalmente al olvidarse de pasar el nombre del programa invocado como parte de la matriz de argumentos execvey funciones similares, y si las personas olvidan habitualmente protegerse contra él, también puede ocurrir. terminan siendo el último paso en una vulnerabilidad explotable maliciosamente que permite a un atacante obtener un shell interactivo.
mtraceur

161

La línea shebang nunca se ha especificado como parte de POSIX, SUS, LSB o cualquier otra especificación. AFAIK, ni siquiera se ha documentado adecuadamente.

Existe un consenso aproximado sobre lo que hace: tomar todo entre el !y el \ny el exec. Se asume que todo lo que se encuentra entre el !y el \nes una ruta absoluta y completa al intérprete. No hay consenso sobre lo que sucede si contiene espacios en blanco.

  1. Algunos sistemas operativos simplemente tratan todo como una ruta. Después de todo, en la mayoría de los sistemas operativos, los espacios en blanco o los guiones son legales en una ruta.
  2. Algunos sistemas operativos se dividen en espacios en blanco y tratan la primera parte como la ruta hacia el intérprete y el resto como argumentos individuales.
  3. Algunos sistemas operativos se dividen en el primer espacio en blanco y tratan la parte frontal como la ruta al intérprete y el resto como un solo argumento (que es lo que está viendo).
  4. Algunos incluso no admiten las 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 pasar más de un argumento.

Y puesto que la ubicación de los comandos también no se especifica en POSIX o SUS, se utiliza generalmente hasta que solo argumento mediante el paso del ejecutable de nombre a envfin de que se 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 vive /bin, por lo que generalmente es seguro. La ubicación de enves mucho más estandarizada que la ubicación de, gawko incluso peor, algo como pythono rubyo spidermonkey.]

Lo que significa que en realidad no puede utilizar ningún argumento en absoluto .


1
El env de FreeBSD tiene un -Sinterruptor que ayuda aquí, pero no está presente en mi Linux env, y sospecho que tampoco está disponible en gygwin. @hstoerr, es posible que otros usuarios con diferentes situaciones lean sus preguntas más tarde, por lo que, en general, son preferibles las respuestas portátiles, incluso si ahora no necesita la portabilidad.
dubiousjim

4
Entonces, no podemos usar argumentos de manera portátil en un shebang. Pero, ¿y si necesitamos argumentos por cualquier medio necesario? Supongo que la solución es escribir un script de shell contenedor que contenga #!/bin/shy /usr/bin/env gawk --re-interval -f my-script.awk. ¿Es eso correcto?
Rory O'Kane

1
No estoy de acuerdo. Puede usar un argumento de manera bastante portátil. Cualquier sistema en el que no pueda usar ningún argumento fracasa estrepitosamente en la implementación de este Unixismo tradicional, que es el hash-bang. Si las no implementaciones son un juego limpio, entonces podemos decir con seguridad que en #!sí mismo no es portátil. Por ejemplo, Windows no reconoce esta convención "nativamente" en absoluto. Tradicionalmente, se necesita un argumento único en Unix para poder hacerlo #!/usr/bin/awk -f.
Kaz

7
@Kaz: Sí, pero dado que las rutas de muchos binarios no están estandarizadas, usa su único argumento para #!/usr/bin/env rubyo los me gusta.
Jörg W Mittag

3
@Pacerier: cambie la especificación POSIX y espere de 20 a 30 años hasta que todos los sistemas se hayan actualizado para cumplir con la especificación.
Jörg W Mittag

18

Aunque no es exactamente portátil, a partir de coreutils 8.30 y según su documentación podrás utilizar:

#!/usr/bin/env -S command arg1 arg2 ...

Así dado:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

conseguirás:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

y por si tienes curiosidad showargses:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

Respuesta original aquí .


1
FYI, FreeBSD ha tenido -S durante años (desde 6.0). Esta es una adición de portabilidad bienvenida a coreutils.
Juan

12

Me encontré con el mismo problema, sin una solución aparente debido a la forma en que se tratan los espacios en blanco en un shebang (al menos en Linux).

Sin embargo, puede pasar varias opciones en un shebang, siempre que sean opciones cortas y se puedan concatenar (al 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 aceptan argumentos.


11

En Cygwin y Linux, todo lo que sigue a la ruta del shebang se analiza en el programa como un argumento.

Es posible solucionar esto usando otro awkscript dentro del shebang:

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

Esto se ejecutará {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}en awk.
Y esto se ejecutará /usr/bin/gawk --re-interval -f path/to/your/script.awken el shell de su sistema.


2
esto no funcionará si ha pasado argumentos al guión
Steven Penny

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

El truco de shebang de shell anterior es más portátil que /usr/bin/env.


El '' ':' es una retención porque mi solución original era para una secuencia de comandos de Python, por lo que '' ':' le dice al intérprete de Python que ignore la parte ejecutiva.
user3123730

4
Creo que está siendo rechazado porque su solución es a favor python, pero esta pregunta es sobre awk.
Aaron McDaid

1
Gran truco para Python.
Zaar Hai

3

En el manual de gawk (http://www.gnu.org/manual/gawk/gawk.html), al final de la sección 1.14, tenga en cuenta que solo debe usar un único argumento cuando ejecute gawk desde una línea shebang. Dice que el sistema operativo tratará todo después de la ruta para mirar boquiabierto como un solo argumento. ¿Quizás haya otra forma de especificar la --re-intervalopción? Quizás su script pueda hacer referencia a su shell en la línea shebang, ejecutarse gawkcomo un comando e incluir el texto de su script como un "documento aquí".


Parece que no hay otra forma de especificar la opción. Tiene razón: gawk -f - << EOF, algunas líneas de scripts, EOF funciona, pero me impide leer la entrada estándar con gawk.
Hans-Peter Störr

El documento here consume el flujo de entrada estándar gawk, pero es posible que aún pueda canalizar algo a través de stderr (es decir, redirigir stdout a stderr antes de canalizar a este script). En realidad, nunca lo he intentado, pero siempre que el primer proceso no emita nada en stderr, podría funcionar. También puede crear una tubería con nombre ( linuxjournal.com/content/using-named-pipes-fifos-bash ) si desea asegurarse de que nada más la esté usando.
bta

3

¿Por qué no usar bashy gawksí mismo, para omitir 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, naturalmente, también podría lograrse con eg sedo tail, pero creo que hay algún tipo de belleza que depende solo de bashy de gawksí mismo;)


0

Simplemente cómico: existe la siguiente solución bastante extraña que redirige stdin y el programa a través de los descriptores de archivo 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 acerca de esto: el shell realiza una expansión de variables en el script, por lo que debe citar cada $ (como se hace en la segunda línea del script) y probablemente más que eso.


-1

Para una solución portátil, use en awklugar de gawk, invoque el shell BOURNE estándar ( /bin/sh) con su shebang, e invoque awkdirectamente, pasando el programa en la línea de comando como un documento aquí en lugar de a través de stdin:

#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF

Nota: no hay -fargumento para awk. Eso deja stdindisponible para awkleer la entrada. Asumiendo que tienesgawk instalado y en su PATH, eso logra todo lo que creo que estaba tratando de hacer con su ejemplo original (suponiendo que quisiera que el contenido del archivo fuera el script awk y no la entrada, que creo que su enfoque shebang lo habría tratado como ).


3
Eso no funcionó para mí. El hombre de bash dice <<< blabla pone blabla en stdin. ¿Quiso decir << - EOF? De cualquier manera, eso también pone el programa en stdin.
Hans-Peter Störr
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.