Passer la variable shell en tant que / pattern / à awk

59

Avoir le suivant dans une de mes fonctions shell:

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

, alors quand appelé comme _process $arg, $argest passé comme $1et utilisé comme motif de recherche. Cela fonctionne de cette façon, car le shell se développe $1à la place du motif awk! lPeut également être utilisé dans le programme awk, en é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?

Après ne fonctionnera pas,

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

, awk ne sera pas interprété /search/comme une variable, mais littéralement.

Branquito
la source

Réponses:

46

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 cela soit plus efficace (il n'est pas nécessaire de lire le fichier en entier)

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

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

Glenn Jackman
la source
C’est exactement ce qui résout ce problème de la manière que j’ai voulu (1er exemple), parce que cela garde la sémantique, ce qui était mon objectif. Merci.
Branquito
1
Je n'ai pas noté la suppression du bloc BEGIN: une variable non affectée est traitée comme 0 dans un contexte numérique ou la chaîne vide sinon. Donc, une variable non assignée sera fausseif (p) ...
glenn jackman
oui j'ai remarqué, il doit être mis à zéro sur le bloc BEGIN à chaque fois, car il sert de commutateur. Mais curieusement, j'ai essayé maintenant d'utiliser le script $0 ~ pattern, et cela ne fonctionne pas, mais avec /'"$1"'/ça fonctionne!? : O
branquito
peut-être que cela a quelque chose à voir avec la façon dont $lineest récupéré, la recherche de modèle est effectuée sur la sortie de whois $line, $linevenant du fichier dans un bloc WHILE DO.
Branquito
S'il vous plaît montrer le contenu de $line- faites-le dans votre question pour un formatage approprié.
Glenn Jackman
17
awk  -v pattern="$1" '$0 ~ pattern'

Problème lié au awkdéveloppement des séquences d’échappement ANSI C (comme \npour les sauts de ligne, les sauts \fde formulaire, les \\barres obliques inverses, etc.) $1. Cela devient donc un problème si $1contient des caractères de barre oblique inverse qui sont communs dans les expressions régulières (avec GNU awk4.2 ou supérieur, les valeurs commençant par @/et se terminant 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 la awkmise en œuvre.

$ 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 awkfonctionnent 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 $apassé tel quel)

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

( \\Changé \et \bchangé à un backspace).

Stéphane Chazelas
la source
Vous dites donc que si le modèle consistait par exemple \d{3}à trouver trois chiffres, cela ne fonctionnerait pas comme prévu si je vous comprenais bien?
Branquito
2
pour \dlequel n’est pas une séquence d’échappement C valide, cela dépend de votre awkimplémentation (exécutez awk -v 'a=\d{3}' 'BEGIN{print a}'pour vérifier). Mais pour \` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d` comme signifiant un chiffre).
Stéphane Chazelas
il dit: awk warning - séquence d'échappement \d' treated as plain d '{3}, alors j'imagine que j'aurais un problème dans ce cas?
Branquito
1
Désolé, mon mauvais, j'avais une faute de frappe dans ma réponse. Le nom de la variable d'environnement alors doit correspondre ENVIRON["PATTERN"]à la PATTERNvariable d'environnement. Si vous souhaitez utiliser une variable shell, vous devez d'abord l'exporter ( export variable) ou utiliser la ENV=VALUE awk '...ENVIRON["ENV"]'syntaxe de transmission env-var comme dans ma réponse.
Stéphane Chazelas
1
Parce que vous devez exporter une variable shell pour pouvoir la transmettre dans l'environnement à une commande.
Stéphane Chazelas
5

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" }'
Hunter Eidson
la source
Si cela se comporte comme /regex/en termes de recherche de modèle, cela pourrait être une bonne solution. Je vais essayer.
Branquito
1
Les tests rapides que j'ai effectués semblaient fonctionner de la même façon, mais je ne vais même pas commencer à le garantir ... :)
Hunter Eidson
0

Non, mais vous pouvez simplement interpoler le motif dans la chaîne entre guillemets 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 double awk awk littéral, mais c'est toujours le moyen le plus simple d'accomplir cela.

Kilian Foth
la source
Ce chemin est-il sûr si $patterncontient des espaces, mon exemple ci-dessus fonctionnera puisque $ 1 est protégé par des guillemets doubles "$ 1", sans toutefois préciser ce qui se passe dans votre cas.
Branquito
2
Votre exemple original termine la chaîne entre guillemets à la seconde ', protège ensuite les $1guillemets doubles, puis pointe sur une autre chaîne entre guillemets pour la seconde moitié du programme awk. Si je comprends bien, cela devrait avoir exactement le même effet que protéger les $1guillemets simples extérieurs - awk ne voit jamais les guillemets que vous mettez autour.
Kilian Foth
4
Mais si $patterncontient ^/ {system("rm -rf /")};, alors vous êtes en grande difficulté.
Stéphane Chazelas
est-ce l’inconvénient de cette approche seulement, après avoir tout enveloppé dans ""?
Branquito
-3

Vous pouvez utiliser la fonction eval qui résout dans cet exemple la variable nets avant l'exécution de awk.

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