From: Hongyi Zhao on
Hi all,

In awk, we can substitute "foo" with "bar" ONLY for lines which
contain "baz" using the following code:

awk '/baz/{gsub(/foo/, "bar")}; 1'

Now, I want to know how can I let the baz part get its value from a
variable. I've tried the following code but failed:

awk -v mybaz="baz" '/mybaz/{gsub(/foo/, "bar")}; 1' file

BR.
--
..: Hongyi Zhao [ hongyi.zhao AT gmail.com ] Free as in Freedom :.
From: Thomas 'PointedEars' Lahn on
Hongyi Zhao wrote:

> In awk, we can substitute "foo" with "bar" ONLY for lines which
> contain "baz" using the following code:
>
> awk '/baz/{gsub(/foo/, "bar")}; 1'
>
> Now, I want to know how can I let the baz part get its value from a
> variable. I've tried the following code but failed:
>
> awk -v mybaz="baz" '/mybaz/{gsub(/foo/, "bar")}; 1' file

You should state the implementation and version of awk you are using (awk,
mawk, gawk, ...)

$ echo foobar | awk 'BEGIN {x="/foo/"} $x {gsub("bar", "baz", $0)} 1'
foobaz
$ awk --version | head -n 1
GNU Awk 3.1.5


PointedEars
From: Ed Morton on
On 3/29/2010 10:36 PM, Thomas 'PointedEars' Lahn wrote:
> Hongyi Zhao wrote:
>
>> In awk, we can substitute "foo" with "bar" ONLY for lines which
>> contain "baz" using the following code:
>>
>> awk '/baz/{gsub(/foo/, "bar")}; 1'
>>
>> Now, I want to know how can I let the baz part get its value from a
>> variable. I've tried the following code but failed:
>>
>> awk -v mybaz="baz" '/mybaz/{gsub(/foo/, "bar")}; 1' file
>
> You should state the implementation and version of awk you are using (awk,
> mawk, gawk, ...)
>
> $ echo foobar | awk 'BEGIN {x="/foo/"} $x {gsub("bar", "baz", $0)} 1'
> foobaz
> $ awk --version | head -n 1
> GNU Awk 3.1.5
>
>
> PointedEars

No, that only APPEARS to work because x contains a non-integer string so $x
becomes the value of $0 which is "foobar" which is non-NULL and so a true
condition. Look:

$ echo foobar | awk 'BEGIN {x="/foo/"} $x {gsub("bar", "baz", $0)} 1'
foobaz
$ echo foobar | awk 'BEGIN {x="/rabbit/"} $x {gsub("bar", "baz", $0)} 1'
foobaz
$ echo foobar | awk 'BEGIN {x="whatever"} $x {gsub("bar", "baz", $0)} 1'
foobaz
$ echo foobar | awk 'BEGIN {x="2"} $x {gsub("bar", "baz", $0)} 1'
foobar

That last one didn't produce a true condition as x is 2 so $x is $2 which
evaluates to a NULL string as there's just one field of input.

The right way to do that is:

$ echo foobar | awk 'BEGIN{x="foo"} $0 ~ x{gsub("bar", "baz", $0)} 1'
foobaz

so for the OP the solution to the question you posted would be:

awk -v mybaz="baz" '$0 ~ mybaz{gsub(/foo/, "bar")} 1' file

Regards,

Ed.
From: Thomas 'PointedEars' Lahn on
Ed Morton wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Hongyi Zhao wrote:
>>> In awk, we can substitute "foo" with "bar" ONLY for lines which
>>> contain "baz" using the following code:
>>>
>>> awk '/baz/{gsub(/foo/, "bar")}; 1'
>>>
>>> Now, I want to know how can I let the baz part get its value from a
>>> variable. I've tried the following code but failed:
>>>
>>> awk -v mybaz="baz" '/mybaz/{gsub(/foo/, "bar")}; 1' file
>>
>> You should state the implementation and version of awk you are using
>> (awk, mawk, gawk, ...)
>>
>> $ echo foobar | awk 'BEGIN {x="/foo/"} $x {gsub("bar", "baz", $0)} 1'
>> foobaz
>> $ awk --version | head -n 1
>> GNU Awk 3.1.5
>> [...]
>
> No, that only APPEARS to work because x contains a non-integer string so
> $x becomes the value of $0 which is "foobar" which is non-NULL and so a
> true condition. Look:
> [...]

Thank you, it turned out to be too simple to be true. I had read

| ~ !~ Regular expression match, negated match. NOTE: Do not use
| a constant regular expression (/foo/) on the left-hand side
| of a ~ or !~. Only use one on the right-hand side. The
| expression /foo/ ~ exp has the same meaning as (($0 ~
| /foo/) ~ exp). This is usually not what was intended.

but did not expect the `x' in a standalone `$x' to be expanded. Probably
I'm spoiled by (ba)sh ;-)

> The right way to do that is:
>
> $ echo foobar | awk 'BEGIN{x="foo"} $0 ~ x{gsub("bar", "baz", $0)} 1'
> foobaz
>
> so for the OP the solution to the question you posted would be:
>
> awk -v mybaz="baz" '$0 ~ mybaz{gsub(/foo/, "bar")} 1' file

ACK. I am still a bit puzzled about the trailing `1', though. Is it the
equivalent of a true-value relational expression with a missing action
equivalent to `{print}' as described in section "PATTERNS AND ACTIONS" of
the (gawk) manpage? I used to use `{print}' explicitly instead:

awk -v mybaz="baz" '$0 ~ mybaz {gsub(/foo/, "bar")} {print}' file

Also, is there an advantage to use -v over a BEGIN block when there is no
BEGIN block to refer to the variable? Is there another disadvantage, e.g.
reduced compatibility?

TIA.


PointedEars
From: Ed Morton on
On 3/30/2010 5:32 AM, Thomas 'PointedEars' Lahn wrote:
> Ed Morton wrote:
>
>> Thomas 'PointedEars' Lahn wrote:
>>> Hongyi Zhao wrote:
>>>> In awk, we can substitute "foo" with "bar" ONLY for lines which
>>>> contain "baz" using the following code:
>>>>
>>>> awk '/baz/{gsub(/foo/, "bar")}; 1'
>>>>
>>>> Now, I want to know how can I let the baz part get its value from a
>>>> variable. I've tried the following code but failed:
>>>>
>>>> awk -v mybaz="baz" '/mybaz/{gsub(/foo/, "bar")}; 1' file
>>>
>>> You should state the implementation and version of awk you are using
>>> (awk, mawk, gawk, ...)
>>>
>>> $ echo foobar | awk 'BEGIN {x="/foo/"} $x {gsub("bar", "baz", $0)} 1'
>>> foobaz
>>> $ awk --version | head -n 1
>>> GNU Awk 3.1.5
>>> [...]
>>
>> No, that only APPEARS to work because x contains a non-integer string so
>> $x becomes the value of $0 which is "foobar" which is non-NULL and so a
>> true condition. Look:
>> [...]
>
> Thank you, it turned out to be too simple to be true. I had read
>
> | ~ !~ Regular expression match, negated match. NOTE: Do not use
> | a constant regular expression (/foo/) on the left-hand side
> | of a ~ or !~. Only use one on the right-hand side. The
> | expression /foo/ ~ exp has the same meaning as (($0 ~
> | /foo/) ~ exp). This is usually not what was intended.
>
> but did not expect the `x' in a standalone `$x' to be expanded. Probably
> I'm spoiled by (ba)sh ;-)

Since you don't use "$" to dereference the value of awk variables (other than
$1, $2, etc.) then doing

x=2
printf "%s\n",x
printf "%s\n",$x

in awk is equivalent to:

x=2
printf "%s\n" "$x"
eval printf '"%s\n"' \"\${$x}\"

in shell IF the value of x is a number. If the value of x is not a number then
in awk $x is the same as $0.

>
>> The right way to do that is:
>>
>> $ echo foobar | awk 'BEGIN{x="foo"} $0 ~ x{gsub("bar", "baz", $0)} 1'
>> foobaz
>>
>> so for the OP the solution to the question you posted would be:
>>
>> awk -v mybaz="baz" '$0 ~ mybaz{gsub(/foo/, "bar")} 1' file
>
> ACK. I am still a bit puzzled about the trailing `1', though. Is it the
> equivalent of a true-value relational expression with a missing action
> equivalent to `{print}' as described in section "PATTERNS AND ACTIONS" of
> the (gawk) manpage?

Yes. These are all equivalent:

awk '1'
awk '1==1'
awk '"foo"'
awk '{print}'
awk '{print $0}'
awk '1==1{print $0}'

I used to use `{print}' explicitly instead:
>
> awk -v mybaz="baz" '$0 ~ mybaz {gsub(/foo/, "bar")} {print}' file
>
> Also, is there an advantage to use -v over a BEGIN block when there is no
> BEGIN block to refer to the variable?

Only that if you in future had to set the awk variable based on a shell variable
then you can easily change this:

awk -v awkvar="foo" '...'
to:
shellvar="foo"
awk -v awkvar="$shellvar" ....'

Is there another disadvantage, e.g.
> reduced compatibility?

No. The only incompatible awk would be old, broken awk (/usr/bin/awk on Solaris)
but no-one should use that for many other reasons anyway so using "-v" is a
decent test to make sure that's not the awk you're picking up.

Ed.

>
> TIA.
>
>
> PointedEars