awk 'processing_script_here' my=file.txt
parece detenerse y esperar indefinidamente ...
¿Qué está pasando aquí y cómo lo hago funcionar?
awk 'processing_script_here' my=file.txt
parece detenerse y esperar indefinidamente ...
¿Qué está pasando aquí y cómo lo hago funcionar?
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 las BEGIN
declaraciones) en lugar de los nombres de los archivos de entrada.
Eso puede ser útil en cosas como:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Donde puede especificar un archivo diferente FS
/ RS
por archivo. También se usa comúnmente en:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Cuál es una versión más segura de:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(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 un awk
nombre de variable válido .
Lo que constituye un nombre de variable válido en awk
es 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/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 como Stéphane=Chazelas.txt
may o may, dependiendo de la awk
implementació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 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:
awk -f file.awk -vfoo=bar.txt
(también se aplica a awk '{code}' -vfoo=bar.txt
las awk
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 modo awk
entiende como entrada estándar ).
Por eso también
#! /usr/bin/awk -f
los shebangs realmente no funcionan. Si bien los var=value
que se pueden solucionar arreglando los ARGV
valores (agregue un ./
prefijo) en una BEGIN
declaració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 awk
y no por el awk
script.
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 awk
soluciona todos esos problemas con su -E
opción.
Después -E
, gawk espera solo la ruta del awk
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:
#! /usr/bin/gawk -E
shebangs donde la lista de argumentos siempre son archivos de entrada (tenga en cuenta que aún puede editar esa ARGV
lista en una BEGIN
declaración).
También puedes usarlo como:
gawk -e '...awk code here...' -E /dev/null *.txt
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.
../foo
, las /path/to/foo
rutas 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 ;-)
./
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ística awk
(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 :
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):
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 usarlo my_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../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, en awk '{exit}' < /etc/passwd
, esperaría awk
volver al final del primer registro exit
para asegurarse de que deja la posición dentro de stdin allí. POSIX requiere eso. /usr/xpg4/bin/awk
lo hace en Solaris, pero gawk
tampoco mawk
parece hacerlo en GNU / Linux.
awk
esa manera.
Para citar la documentación de gawk (énfasis agregado):
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.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 definido awk
, leerá stdin (también evidente a partir de lo strace
que 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/passwd
que el valor de foo
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
en awk '1' foo=bar
así como la comprobación con cat 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