Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Having the following in one of my shell functions:

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

, so when called as _process $arg, $arg gets passed as $1, and used as a search pattern. It works this way, because shell expands $1 in place of awk pattern! Also l can be used inside awk program, being declared with -v l="$line". All fine.

Is it possible in same manner give pattern to search as a variable?

Following will not work,

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

,as awk will not interpret /search/ as a variable, but instead literally.

share|improve this question

4 Answers 4

up vote 6 down vote accepted

Use awk's ~ operator, and you don't need to provide a literal regex on the right-hand side:

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

Although this would be more efficient (don't have to read the whole file)

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

Depending on the pattern, may want grep -Eq "$1"

share|improve this answer
    
This is exactly what solves this in a way I wanted (1st example), because it keeps the semantics, which was my goal. Thanks. –  branquito Mar 21 at 15:30
    
I didn't note the removal of the BEGIN block: an unassigned variable is treated as 0 in a numeric context or the empty string otherwise. So, an unassigned variable will be false in if (p) ... –  glenn jackman Mar 21 at 15:35
    
yes I noticed, it needs to be set on BEGIN block to zero each time, as it serves as a switch. But interestingly I tried now script using $0 ~ pattern, and it does not work, however with /'"$1"'/ it does work!? :O –  branquito Mar 21 at 15:42
    
maybe it has something to do with the way $line is retrieved, pattern search is done on the output of whois $line, $line coming from file in a WHILE DO block. –  branquito Mar 21 at 15:53
    
Please show the contents of $line -- do it in your question for proper formatting. –  glenn jackman Mar 21 at 15:57

Try something like:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'
share|improve this answer
    
If this behaves same as /regex/ in terms of finding pattern, this could be a nice solution. I will try. –  branquito Mar 21 at 15:22
1  
The quick tests I ran seemed to work the same, but I won't even begin to guarantee it... :) –  Hunter Eidson Mar 21 at 15:24
awk  -v pattern="$1" '$0 ~ pattern'

Has an issue in that awk expands the ANSI C escape sequences in $1. So it becomes an issue if $1 contains backslash characters which is common in regular expressions. Another approach that doesn't suffer from that issue is to write it:

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

How bad it's going to be will depend on the awk implementation.

$ 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 `.'

All awks work the same for valid escape sequences though:

$ a='\\-\b' awk 'BEGIN {print ENVIRON["a"]}' | od -tc
0000000   \   \   -   \   b  \n
0000006
$ awk -v a='\\-\b' 'BEGIN {print a}' | od -tc
0000000   \   -  \b  \n
0000004
share|improve this answer
    
So you are saying that if pattern was for example \d{3} to find three digits, that wouldn't work as expected, if I understood you well? –  branquito Mar 21 at 16:24
1  
for \d which is not a valid C escape sequence, that depends on your awk implementation (run awk -v 'a=\d{3}' 'BEGIN{print a}' to check). But for \` or \b, yes definitely. (BTW, I don't know of any awk implementations that understands \d` as meaning a digit). –  Stéphane Chazelas Mar 21 at 16:30
    
it says: awk warning - escape sequence \d' treated as plain d' d{3}, so I guess I would have a problem in this case? –  branquito Mar 21 at 16:34
1  
Sorry, my bad, I had a typo in my answer. The name of then environment variable has to match ENVIRON["PATTERN"] for the PATTERN environment variable. If you want to use a shell variable, you need to export it first (export variable) or use the ENV=VALUE awk '...ENVIRON["ENV"]' env-var passing syntax as in my answer. –  Stéphane Chazelas Mar 21 at 17:03
1  
Because you need to export a shell variable for it to be passed in the environment to a command. –  Stéphane Chazelas Mar 21 at 17:20

No, but you can simply interpolate the pattern into the double-quoted string you pass to awk:

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

Note that you now have to escape the double-quoted awk literal, but it is still the simplest way of accomplishing this.

share|improve this answer
    
Is this way safe if $pattern contains spaces, my example from above will work as $1 is protected with "$1" double quotes, however not shure what happens in your case. –  branquito Mar 21 at 15:14
1  
Your original example ends the single-quoted string at the second ', then protects the $1 via double quotes and then tacks another single-quoted string for the second half of the awk program. If I understand correctly, this should have exactly the same effect as protecting the $1 via the outer single quotes - awk never sees the double quotes that you put around it. –  Kilian Foth Mar 21 at 15:26
1  
But if $pattern contains ^/ {system("rm -rf /")};, then you're in big trouble. –  Stéphane Chazelas Mar 21 at 16:17
    
is that downside of this approach only, having all wrapped in "" ? –  branquito Mar 21 at 16:27

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.