Como dice Chris , los argumentos del formulario variablename=anythingse tratan como asignación de variables (que se realizan en el momento en que se procesan los argumentos en lugar de los (más nuevos) -v var=valueque se realizan antes de las BEGINdeclaraciones) en lugar de los nombres de los archivos de entrada.
Pero eso se interpone cuando tienes archivos cuyo nombre contiene =caracteres.
Ahora, eso es solo un problema cuando lo que queda del primero =es un awknombre de variable válido .
Lo que constituye un nombre de variable válido en awkes más estricto que en sh.
POSIX requiere que sea algo como:
[_a-zA-Z][_a-zA-Z0-9]*
Con solo caracteres del juego de caracteres portátil. Sin embargo, /usr/xpg4/bin/awkal menos Solaris 11 no es compatible en ese sentido y permite cualquier carácter alfabético en la configuración regional en nombres de variables, no solo a-zA-Z.
Por lo tanto, un argumento como x+y=fooo =baro ./foo=bartodavía se trata como un nombre de archivo de entrada y no una asignación, ya que lo que queda del primero =no es un nombre de variable válido. Un argumento como Stéphane=Chazelas.txtmay o may, dependiendo de la awkimplementación y el entorno local.
Es por eso que con awk, se recomienda usar:
awk '...'./*.txt
en lugar de
awk '...'*.txt
por ejemplo, para evitar el problema si no puede garantizar que el nombre de los txtarchivos no contendrá =caracteres.
Además, tenga en cuenta que un argumento como -vfoo=bar.txtpuede ser tratado como una opción si usa:
awk -f file.awk -vfoo=bar.txt
(también se aplica a awk '{code}' -vfoo=bar.txtlas awkversiones de busybox anteriores a 1.28.0, consulte el informe de error correspondiente ).
Una vez más, el uso ./*.txtfunciona alrededor de eso (el uso de un ./prefijo también ayuda con un archivo llamado -que de otro modo awkentiende como entrada estándar ).
Por eso también
#! /usr/bin/awk -f
los shebangs realmente no funcionan. Si bien los var=valueque se pueden solucionar arreglando los ARGVvalores (agregue un ./prefijo) en una BEGINdeclaración:
#! /usr/bin/awk -f
BEGIN {for(i =1; i < ARGC; i++)if(ARGV[i]~/^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i]="./" ARGV[i]}# rest of awk script
Eso no ayudará con las opciones, ya que esas son vistas por ellos awky no por el awkscript.
Un problema cosmético potencial con el uso de ese ./prefijo es que termina en FILENAME, pero siempre puede usar substr(FILENAME, 3)para quitarlo si no lo desea.
La implementación de GNU awksoluciona todos esos problemas con su -Eopción.
Después -E, gawk espera solo la ruta del awkscript (donde -todavía significa stdin) y luego una lista de rutas de archivos de entrada solamente (y allí, ni siquiera -se trata especialmente).
Está especialmente diseñado para:
#! /usr/bin/gawk -E
shebangs donde la lista de argumentos siempre son archivos de entrada (tenga en cuenta que aún puede editar esa ARGVlista en una BEGINdeclaración).
También puedes usarlo como:
gawk -e '...awk code here...'-E /dev/null *.txt
Lo usamos -Econ un script vacío ( /dev/null) solo para asegurarnos de que los *.txtposteriores se traten siempre como archivos de entrada, incluso si contienen =caracteres.
No veo cómo la ruta explícita que termina en FILENAME es un problema. O bien, el script awk es general, en cuyo caso debe manejar todo tipo de rutas que terminan en FILENAME (incluidas, entre otras ../foo, las /path/to/foorutas que están en una codificación diferente), en cuyo caso substr(FILENAME,3)no será suficiente, o es una secuencia de comandos de una sola toma en la que el usuario básicamente sabe cuáles son los nombres de archivo, en cuyo caso probablemente no debería molestarse con ninguno de ellos que contenga =ninguno ;-)
mosvy
2
@mosvy No creo que indique tanto que ./es un problema, pero que puede ser indeseable bajo ciertas condiciones, como casos en los que el nombre de archivo debe incluirse en la salida, en cuyo caso ./debe ser redundante e innecesario, por lo que Tendré que deshacerme de él de alguna manera. Aquí hay al menos un ejemplo . En cuanto a que el usuario sepa qué son los nombres de archivo, bueno, en este caso también sabemos qué nombre de archivo es, pero =aún se interpone en el proceso adecuado. Entonces, el liderazgo puede -interponerse en el camino
No es solo el local (relativo a este directorio) ./sino también el global (ruta absoluta) lo /que hace que awk interprete el argumento como un archivo.
Isaac
21
En la mayoría de las versiones de awk, los argumentos después del programa a ejecutar son:
Un archivo
Una asignación del formulario x=y
Como su nombre de archivo se interpreta como el caso n. ° 2, awk todavía está esperando que se lea algo en stdin (ya que no percibe que se haya pasado ningún nombre de archivo).
Cualquiera de los siguientes dos tipos de argumentos se pueden mezclar:
archivo: un nombre de ruta de un archivo que contiene la entrada a leer, que coincide con el conjunto de patrones en el programa. Si no se especifican operandos de archivo, o si un operando de archivo es '-', se utilizará la entrada estándar.
asignación: un operando que comienza con un carácter de subrayado o alfabético del conjunto de caracteres portátil (consulte la tabla en el volumen de Definiciones básicas de IEEE Std 1003.1-2001, Sección 6.1, Conjunto de caracteres portátil), seguido de una secuencia de caracteres de subrayado, dígitos, y el alfabeto del juego de caracteres portátil, seguido del carácter '=', especificará una asignación de variable en lugar de un nombre de ruta.
Como tal, de forma portátil, tiene algunas opciones (es probable que el n. ° 1 sea el menos intrusivo):
Use awk ... ./my=file, que evita esto ya .que no es "un carácter de subrayado o alfabético del conjunto de caracteres portátil".
Ponga el archivo en stdin usando awk ... < my=file. Sin embargo, esto no funciona bien con varios archivos.
Haga un enlace fijo al archivo temporalmente y úselo. Puede hacer algo como ln my=file my_file, y luego usarlo my_filenormalmente. No se realizará ninguna copia, y ambos archivos estarán respaldados por los mismos datos y metadatos de inodo. Después de usarlo, es seguro eliminar el enlace creado ya que el número de referencias al inodo seguirá siendo mayor que 0.
No ./my=file funciona % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory). Esto debería ser portátil porque ./myno es un nombre de variable válido, por lo que no debe analizarse de esa manera.
Stephen Harris
2
Como dice el texto POSIX, el problema es solo cuando el primero =está precedido por un carácter de subrayado o alfabético del conjunto de caracteres portátil (consulte la tabla en el volumen de Definiciones básicas de IEEE Std 1003.1-2001, Sección 6.1, Conjunto de caracteres portátil), seguido de una secuencia de guiones bajos, dígitos y alfabéticos del juego de caracteres portátil . así que una ruta de archivo como ++foo=bar.txto =fooo ./foo=barestán todas bien como eso .o +no es a [_a-zA-Z].
Stéphane Chazelas
1
@SergiyKolodyazhnyy awk es externo al shell, por lo que no importa cuál use. ./my=fileserá pasado literalmente.
Chris Down
1
@SergiyKolodyazhnyy, lo mismo para awk '{print $1,$2}' /etc/passwd. El punto es que hacer que el shell abra el archivo en lugar de awk no hace ninguna diferencia en cuanto a si lo hace buscable o no. En realidad, en awk '{exit}' < /etc/passwd, esperaría awkvolver al final del primer registro exitpara asegurarse de que deja la posición dentro de stdin allí. POSIX requiere eso. /usr/xpg4/bin/awklo hace en Solaris, pero gawktampoco mawkparece hacerlo en GNU / Linux.
Stéphane Chazelas
3
@mosvy, vea la sección INPUT ARCHIVOS en pubs.opengroup.org/onlinepubs/9699919799/utilities/… Es útil en una serie de patrones de uso que solo tienen sentido con archivos normales como cuando desea truncar un archivo o escribir datos en él en una posición identificada de awkesa manera.
Cualquier argumento adicional en la línea de comando normalmente se trata como archivos de entrada para ser procesados en el orden especificado. Sin embargo, un argumento que tiene la forma var = value, asigna el valor del valor a la variable var; no especifica un archivo en absoluto.
¿Por qué el comando se detiene y espera? Debido a que en el formulario awk 'processing_script_here' my=file.txtno hay un archivo especificado por la definición anterior, my=file.txtse interpreta como asignación de variable, y si no hay un archivo definido awk, leerá stdin (también evidente a partir de lo straceque muestra que awk en dicho comando está esperando en read(0,'...)syscall.
Esto también está documentado en las especificaciones de POSIX awk , consulte la sección OPERANDS y parte de las asignaciones de eso)
La asignación variable es evidente en awk '{print foo}' foo=bar /etc/passwdque el valor de foose imprime para cada línea en / etc / passwd. ./foo=barSin embargo, la ruta específica o completa funciona.
Tenga en cuenta que se ejecuta straceen awk '1' foo=barasí como la comprobación con cat foo=barespectáculos que esto es cuestión awk-específica, y execve sí muestra nombre de archivo como argumento pasado, por lo que los depósitos no tienen nada que ver con la asignación de variables env en este caso.
Además, tenga en cuenta que awk '...script...' foo=barno provocará la creación de variables de entorno por shell, ya que las asignaciones de variables de entorno deben preceder a un comando para que surta efecto. Consulte las Reglas de gramática de shell POSIX , punto número 7. Además, esto se puede verificar a través deawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd
Respuestas:
Como dice Chris , los argumentos del formulario
variablename=anything
se tratan como asignación de variables (que se realizan en el momento en que se procesan los argumentos en lugar de los (más nuevos)-v var=value
que se realizan antes de lasBEGIN
declaraciones) en lugar de los nombres de los archivos de entrada.Eso puede ser útil en cosas como:
Donde puede especificar un archivo diferente
FS
/RS
por archivo. También se usa comúnmente en:Cuál es una versión más segura de:
(que no funciona si
file1
está vacío)Pero eso se interpone cuando tienes archivos cuyo nombre contiene
=
caracteres.Ahora, eso es solo un problema cuando lo que queda del primero
=
es unawk
nombre de variable válido .Lo que constituye un nombre de variable válido en
awk
es más estricto que ensh
.POSIX requiere que sea algo como:
Con solo caracteres del juego de caracteres portátil. Sin embargo,
/usr/xpg4/bin/awk
al menos Solaris 11 no es compatible en ese sentido y permite cualquier carácter alfabético en la configuración regional en nombres de variables, no solo a-zA-Z.Por lo tanto, un argumento como
x+y=foo
o=bar
o./foo=bar
todavía se trata como un nombre de archivo de entrada y no una asignación, ya que lo que queda del primero=
no es un nombre de variable válido. Un argumento comoStéphane=Chazelas.txt
may o may, dependiendo de laawk
implementación y el entorno local.Es por eso que con awk, se recomienda usar:
en lugar de
por ejemplo, para evitar el problema si no puede garantizar que el nombre de los
txt
archivos no contendrá=
caracteres.Además, tenga en cuenta que un argumento como
-vfoo=bar.txt
puede ser tratado como una opción si usa:(también se aplica a
awk '{code}' -vfoo=bar.txt
lasawk
versiones de busybox anteriores a 1.28.0, consulte el informe de error correspondiente ).Una vez más, el uso
./*.txt
funciona alrededor de eso (el uso de un./
prefijo también ayuda con un archivo llamado-
que de otro modoawk
entiende como entrada estándar ).Por eso también
los shebangs realmente no funcionan. Si bien los
var=value
que se pueden solucionar arreglando losARGV
valores (agregue un./
prefijo) en unaBEGIN
declaración:Eso no ayudará con las opciones, ya que esas son vistas por ellos
awk
y no por elawk
script.Un problema cosmético potencial con el uso de ese
./
prefijo es que termina enFILENAME
, pero siempre puede usarsubstr(FILENAME, 3)
para quitarlo si no lo desea.La implementación de GNU
awk
soluciona todos esos problemas con su-E
opción.Después
-E
, gawk espera solo la ruta delawk
script (donde-
todavía significa stdin) y luego una lista de rutas de archivos de entrada solamente (y allí, ni siquiera-
se trata especialmente).Está especialmente diseñado para:
shebangs donde la lista de argumentos siempre son archivos de entrada (tenga en cuenta que aún puede editar esa
ARGV
lista en unaBEGIN
declaración).También puedes usarlo como:
Lo usamos
-E
con un script vacío (/dev/null
) solo para asegurarnos de que los*.txt
posteriores se traten siempre como archivos de entrada, incluso si contienen=
caracteres.fuente
../foo
, las/path/to/foo
rutas que están en una codificación diferente), en cuyo casosubstr(FILENAME,3)
no será suficiente, o es una secuencia de comandos de una sola toma en la que el usuario básicamente sabe cuáles son los nombres de archivo, en cuyo caso probablemente no debería molestarse con ninguno de ellos que contenga=
ninguno ;-)./
es un problema, pero que puede ser indeseable bajo ciertas condiciones, como casos en los que el nombre de archivo debe incluirse en la salida, en cuyo caso./
debe ser redundante e innecesario, por lo que Tendré que deshacerme de él de alguna manera. Aquí hay al menos un ejemplo . En cuanto a que el usuario sepa qué son los nombres de archivo, bueno, en este caso también sabemos qué nombre de archivo es, pero=
aún se interpone en el proceso adecuado. Entonces, el liderazgo puede-
interponerse en el camino./
prefijo para evitar esa característicaawk
(incorrecta), pero luego terminas con un./
resultado en el que quizás quieras quitar. ¿ Ves cómo verificar si la primera línea del archivo contiene una cadena específica? como ejemplo../
sino también el global (ruta absoluta) lo/
que hace que awk interprete el argumento como un archivo.En la mayoría de las versiones de awk, los argumentos después del programa a ejecutar son:
x=y
Como su nombre de archivo se interpreta como el caso n. ° 2, awk todavía está esperando que se lea algo en stdin (ya que no percibe que se haya pasado ningún nombre de archivo).
Portablemente, este comportamiento está documentado en POSIX :
Como tal, de forma portátil, tiene algunas opciones (es probable que el n. ° 1 sea el menos intrusivo):
awk ... ./my=file
, que evita esto ya.
que no es "un carácter de subrayado o alfabético del conjunto de caracteres portátil".awk ... < my=file
. Sin embargo, esto no funciona bien con varios archivos.ln my=file my_file
, y luego usarlomy_file
normalmente. No se realizará ninguna copia, y ambos archivos estarán respaldados por los mismos datos y metadatos de inodo. Después de usarlo, es seguro eliminar el enlace creado ya que el número de referencias al inodo seguirá siendo mayor que 0.fuente
./my=file
funciona% awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Esto debería ser portátil porque./my
no es un nombre de variable válido, por lo que no debe analizarse de esa manera.=
está precedido por un carácter de subrayado o alfabético del conjunto de caracteres portátil (consulte la tabla en el volumen de Definiciones básicas de IEEE Std 1003.1-2001, Sección 6.1, Conjunto de caracteres portátil), seguido de una secuencia de guiones bajos, dígitos y alfabéticos del juego de caracteres portátil . así que una ruta de archivo como++foo=bar.txt
o=foo
o./foo=bar
están todas bien como eso.
o+
no es a[_a-zA-Z]
../my=file
será pasado literalmente.awk '{print $1,$2}' /etc/passwd
. El punto es que hacer que el shell abra el archivo en lugar de awk no hace ninguna diferencia en cuanto a si lo hace buscable o no. En realidad, enawk '{exit}' < /etc/passwd
, esperaríaawk
volver al final del primer registroexit
para asegurarse de que deja la posición dentro de stdin allí. POSIX requiere eso./usr/xpg4/bin/awk
lo hace en Solaris, perogawk
tampocomawk
parece hacerlo en GNU / Linux.awk
esa manera.Para citar la documentación de gawk (énfasis agregado):
¿Por qué el comando se detiene y espera? Debido a que en el formulario
awk 'processing_script_here' my=file.txt
no hay un archivo especificado por la definición anterior,my=file.txt
se interpreta como asignación de variable, y si no hay un archivo definidoawk
, leerá stdin (también evidente a partir de lostrace
que muestra que awk en dicho comando está esperando enread(0,'...)
syscall.Esto también está documentado en las especificaciones de POSIX awk , consulte la sección OPERANDS y parte de las asignaciones de eso)
La asignación variable es evidente en
awk '{print foo}' foo=bar /etc/passwd
que el valor defoo
se imprime para cada línea en / etc / passwd../foo=bar
Sin embargo, la ruta específica o completa funciona.Tenga en cuenta que se ejecuta
strace
enawk '1' foo=bar
así como la comprobación concat foo=bar
espectáculos que esto es cuestión awk-específica, y execve sí muestra nombre de archivo como argumento pasado, por lo que los depósitos no tienen nada que ver con la asignación de variables env en este caso.Además, tenga en cuenta que
awk '...script...' foo=bar
no provocará la creación de variables de entorno por shell, ya que las asignaciones de variables de entorno deben preceder a un comando para que surta efecto. Consulte las Reglas de gramática de shell POSIX , punto número 7. Además, esto se puede verificar a través deawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd
fuente