web-dev-qa-db-fra.com

Passer la variable Shell en tant que / motif / à awk

Avoir les éléments suivants dans l'une de mes fonctions Shell:

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

, donc lorsqu'il est appelé en tant que _process $arg, $arg est passé en tant que $1, et utilisé comme modèle de recherche. Cela fonctionne de cette façon, car Shell se développe $1 à la place du modèle awk! l peut également être utilisé dans le programme awk, étant déclaré avec -v l="$line". Tout va bien.

Est-il possible de la même manière de donner un motif de recherche en tant que variable?

La suite ne fonctionnera pas,

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

, comme awk n'interprète pas /search/ comme variable, mais à la place littéralement.

62
branquito

Utilisez l'opérateur ~ De awk, et vous n'avez pas besoin de fournir une expression rationnelle littérale sur le côté droit:

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

Bien que ce soit plus efficace (pas besoin de lire tout le fichier)

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

Selon le motif, vous voudrez peut-être grep -Eq "$1"

48
glenn jackman
awk  -v pattern="$1" '$0 ~ pattern'

A un problème dans la mesure où awk développe les séquences d'échappement ANSI C (comme \n pour la nouvelle ligne, \f pour le flux de formulaires, \\ pour la barre oblique inverse, etc.) dans $1. Cela devient donc un problème si $1 contient des barres obliques inverses courantes dans les expressions régulières (avec GNU awk 4.2 ou supérieur, valeurs commençant par @/ et se termine par /, sont également un problème ). Une autre approche qui ne souffre pas de ce problème est de l'écrire:

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

La gravité de la situation dépendra de l'implémentation de awk.

$ 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

Tous les awk fonctionnent de la même manière pour les séquences d'échappement valides:

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

(contenu de $a passé tel quel)

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

(\\ changé en \ et \b changé en caractère de retour arrière).

18
Stéphane Chazelas

Essayez quelque chose comme:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'
5
Hunter Eidson

Non, mais vous pouvez simplement interpoler le motif dans la chaîne entre guillemets doubles que vous passez à awk:

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

Notez que vous devez maintenant échapper au littéral awk entre guillemets doubles, mais c'est toujours le moyen le plus simple d'y parvenir.

0
Kilian Foth