Pase la variable de shell como a / patrón / a awk

59

Tener lo siguiente en una de mis funciones de shell:

function _process () {
  awk -v l="$line" '
  BEGIN {p=0}
  /'"$1"'/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '
}

, así que cuando se llama como _process $arg, $argse pasa como $1y se usa como patrón de búsqueda. Funciona de esta manera, ¡porque la carcasa se expande $1en lugar del patrón awk! También lse puede usar dentro del programa awk, declarándose con -v l="$line". Todo muy bien.

¿Es posible de la misma manera dar un patrón para buscar como variable?

Seguir no funcionará,

awk -v l="$line" -v search="$pattern" '
  BEGIN {p=0}
  /search/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '

, como awk no se interpretará /search/como una variable, sino literalmente.

branquito
fuente

Respuestas:

46

Utilice el ~operador de awk , y no necesita proporcionar una expresión regular literal en el lado derecho:

function _process () {
    awk -v l="$line" -v pattern="$1" '
        $0 ~ pattern {p=1} 
        END {if(p) print l >> "outfile.txt"}
    '  
}

Aunque esto sería más eficiente (no tiene que leer todo el archivo)

function _process () {
    grep -q "$1" && echo "$line"
}

Dependiendo del patrón, puede querer grep -Eq "$1"

Glenn Jackman
fuente
Esto es exactamente lo que resuelve esto de la manera que quería (primer ejemplo), porque mantiene la semántica, que era mi objetivo. Gracias.
branquito
1
No noté la eliminación del bloque BEGIN: una variable no asignada se trata como 0 en un contexto numérico o la cadena vacía de lo contrario. Entonces, una variable no asignada será falsa enif (p) ...
glenn jackman
Sí, lo noté, debe establecerse en el bloque BEGIN a cero cada vez, ya que sirve como un interruptor. Pero, curiosamente, intenté usar script ahora $0 ~ pattern, y no funciona, ¡¿pero /'"$1"'/sí funciona ?! : O
branquito
tal vez tiene algo que ver con la forma en que $linese recupera, la búsqueda de patrones se realiza en la salida de whois $line, $lineproveniente del archivo en un bloque WHILE DO.
branquito
Muestre el contenido de $line: hágalo en su pregunta para obtener el formato adecuado.
Glenn Jackman
17
awk  -v pattern="$1" '$0 ~ pattern'

Tiene un problema que awkexpande las secuencias de escape ANSI C (como \npara nueva línea, \fpara alimentación de formulario, \\para barra diagonal inversa, etc.) en $1. Por lo tanto, se convierte en un problema si $1contiene caracteres de barra diagonal inversa que es común en las expresiones regulares (con GNU awk4.2 o superior, los valores que comienzan @/y terminan en /, también son un problema ). Otro enfoque que no sufre de ese problema es escribirlo:

PATTERN=$1 awk '$0 ~ ENVIRON["PATTERN"]'

Lo malo que será dependerá de la awkimplementación.

$ nawk -v 'a=\.' 'BEGIN {print a}'
.
$ mawk -v 'a=\.' 'BEGIN {print a}'
\.
$ gawk -v 'a=\.' 'BEGIN {print a}'
gawk: warning: escape sequence `\.' treated as plain `.'
.
$ gawk5.0.1 -v 'a=@/foo/' BEGIN {print a}'
foo

Sin awkembargo, todos funcionan igual para secuencias de escape válidas:

$ a='\\-\b' awk 'BEGIN {print ENVIRON["a"]}' | od -tc
0000000   \   \   -   \   b  \n
0000006

(contenido de $aaprobado tal cual)

$ awk -v a='\\-\b' 'BEGIN {print a}' | od -tc
0000000   \   -  \b  \n
0000004

( \\cambiado \y \bcambiado a un carácter de retroceso).

Stéphane Chazelas
fuente
¿Estás diciendo que si el patrón fuera, por ejemplo, \d{3}encontrar tres dígitos, eso no funcionaría como se esperaba si te entendiera bien?
branquito
2
para lo \dcual no es una secuencia de escape C válida, eso depende de su awkimplementación (ejecutar awk -v 'a=\d{3}' 'BEGIN{print a}'para verificar). Pero para \` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d` como significado de un dígito).
Stéphane Chazelas
dice: advertencia awk - secuencia de escape \d' treated as plain d 'd {3}, así que supongo que tendría un problema en este caso?
branquito
1
Lo siento, mi mal, tuve un error tipográfico en mi respuesta. El nombre de la variable de entorno debe coincidir ENVIRON["PATTERN"]con la PATTERNvariable de entorno. Si desea usar una variable de shell, primero debe exportarla ( export variable) o usar la ENV=VALUE awk '...ENVIRON["ENV"]'sintaxis de paso env-var como en mi respuesta.
Stéphane Chazelas
1
Porque necesita exportar una variable de shell para que se pase en el entorno a un comando.
Stéphane Chazelas
5

Intenta algo como:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'
Hunter Eidson
fuente
Si esto se comporta igual que /regex/en términos de búsqueda de patrones, esta podría ser una buena solución. Intentaré.
branquito
1
Las pruebas rápidas que realicé parecían funcionar igual, pero ni siquiera comenzaré a garantizarlo ... :)
Hunter Eidson
0

No, pero puede simplemente interpolar el patrón en la cadena de comillas dobles que pasa a awk:

awk -v l="$line" "BEGIN {p=0}; /$pattern/ {p=1}; END{ if(p) print l >> \"outfile.txt\" }"

Tenga en cuenta que ahora tiene que escapar del literal awk entre comillas dobles, pero sigue siendo la forma más sencilla de lograr esto.

Kilian Foth
fuente
Es seguro de esta manera si $patterncontiene espacios, mi ejemplo de arriba funcionará ya que $ 1 está protegido con comillas dobles "$ 1", sin embargo, no sé qué sucede en su caso.
branquito
2
Su ejemplo original finaliza la cadena de comillas simples en la segunda ', luego protege las $1comillas dobles y luego agrega otra cadena de comillas simples para la segunda mitad del programa awk. Si lo entiendo correctamente, esto debería tener exactamente el mismo efecto que proteger a $1través de las comillas simples externas: awk nunca ve las comillas dobles que coloca a su alrededor.
Kilian Foth
44
Pero si $patterncontiene ^/ {system("rm -rf /")};, entonces estás en un gran problema.
Stéphane Chazelas
¿Es ese el único inconveniente de este enfoque, ya que todo está envuelto en ""?
branquito
-3

Puede usar la función eval que resuelve en este ejemplo la variable nets antes de ejecutar awk.

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
Noxy
fuente