Why a ~ in double quotes within awk caused syntax error - linux

I was going write script for my device.
Here is my initial code:
dev_name=random_sting
major=`awk "\$2 ~ /^${dev_name}\$/ { print \$1 }" /proc/devices`
Then an error happen
awk: ~ /^random_string$/ { print }
awk: ^ syntax error
Meanwhile, I did an experiment:
var1=random_string
echo "\$ /^$var1\$/ \$"
The output was
$ /^random_string$/ $
It seems the syntax should be correct, can anybody give me an answer?

You need additional escapes inside back ticks. Try using major=$( .. ) instead..

In this case you can also bypass the need for escaping, using the -v option of awk, like this
major=`awk -v dev="$dev_name" '$2 ~ dev { print $1 }' /proc/devices`

Your expression inside backticks will pass through 2 shells/unescape stages.
awk "\$2 ~ /^${dev_name}\$/ { print \$1 }" /proc/devices
...will be expanded and unescaped by your bash to...
awk "$2 ~ /^random_string$/ { print $1 }" /proc/devices`
...which the shell started by the backticks will expand and unescape again to...
awk "~ /^random_string$/ { print }" /proc/devices`
...since $1 and $2 are not defined.
What you want to do is to escape $1 and $2 twice;
awk "\\\$2 ~ /^${dev_name}\$/ { print \\\$1 }" /proc/devices
...to make the executed end result...
awk "$2 ~ /^random_string\$/ { print $1 }" /proc/devices

That's how I solve the problem.
Let's scrutinize this line:
dev_name='loop' ; major=` awk "\\\$2 ~ /^\${dev_name}\\\$/ { print \\\$1 }" /proc/devices` ; echo $major
bash expand it twice, and double quote is not expanded between backticks (`), so this is going to output the proper outcome.

Related

Shell Bash Script

I am making a bash script. I have to get 3 variables
VAR1=$(cat /path to my file/ | grep "string1" | awk '{ print $2 }'
VAR2=$(cat /path to my file/ | grep "string2" | awk '{ print $2 }'
VAR3=$(cat /path to my file/ | grep "string3" | awk '{ print $4 }'
My problem is that if I write
echo $VAR1
echo $VAR2
echo $VAR3
I can see values correctly
But when I try to write them in one line like this
echo "VAR1: $VAR1 VAR2: $VAR2 VAR3: $VAR3"
Value from $VAR3 is written at the beginning of output overwritting values of $VAR1 and $VAR2
I expect my explanation had been clear. Any doubt please let me know
Thanks and regards.
Rambert
It seems to me that $VAR3 contains \r which in some shells will move the cursor to the beginning of the line. Use printf instead:
printf "VAR1: %s VAR2: %s VAR3: %s\n" "$VAR1" "$VAR2" "$VAR3"
Also note that the way you extract the values is highly inefficient and can be reduced to one call to awk:
read -r var1 var2 var3 _ < <(awk '/string1/ { a=$2 }
/string2/ { b=$2 }
/string3/ { c=$4 }
END { print(a, b, c) }' /path/to/file)
printf "VAR1: %s VAR2: %s VAR3: %s\n" "$var1" "$var2" "$var3"
A nitpick is that uppercase variable names are reserved for environment variables, so I changed all to lowercase.
<(...) is a process substitution and will make ... write to a "file" and return the file name:
$ echo <(ls)
/dev/fd/63
And command < file is a redirection changing standard input of command to be comming from the file file.
You could write :
cat /path to my file/ | grep "string1" | awk '{ print $2 }'
as
awk '/string1/{print $2}' /path/to/file
In other words you could do with awk alone what you intended to do with cat, grep & awk
So finally get :
VAR1=$(awk '/string1/{print $2}' /path/to/file) #mind the closing ')'
Regarding the issue you face, it looks like you have carriage returns or \r in your variables. In bash echo will not interpret escape sequences without the -e option, but the printf option which [ #andlrc ] pointed out is a good try though as he mentioned in his [ answer ]
which in some shells will move the cursor to the beginning
Notes :
Another subtle point to keep in mind is to avoid using upper case variable names like VAR1 for user scripts. So replace it with var1 or so
When assigning values to variable spaces are not allowed around =, so
VAR1="Note there are no spaces around = sign"
is the right usage

Using Grep or AWK command

I have file with this text information:
http://=
en.domain.com/registration.html#/?doitoken=1D7f1ad404-f84b-4a3b-8931=
-4f40b619730e
http://=
en.domain.com/registration.html#/?doitoken=5D8172f6e6-240f-42e6-8512=
-6d7f6bd61c2d
http://=
en.domain.com/registration.html#/?doitoken=8D8172f6e6-240f-42e6-8512=
-6d7f6bd61c2d
How i can do this using grep or awk command in linux bash:
http://en.domain.com/registration.html#/?doitoken=1D7f1ad404-f84b-4a3b-8931-4f40b619730e
http://en.domain.com/registration.html#/?doitoken=5D8172f6e6-240f-42e6-8512-6d7f6bd61c2d
http://en.domain.com/registration.html#/?doitoken=8D8172f6e6-240f-42e6-8512-6d7f6bd61c2d
Thanks for your answers!
awk 'BEGIN{FS="=\n"; RS=""; OFS=""} {print $1, $2, $3}' input_file
You could also get rid of OFS="" and remove the ,s in the print statement
Save the program as pr.awk, and run awk -f pr.awk input.dat
NF {
n++
sub(/=$/, "")
ans = ans $0
}
n==3 { # flush
print ans
ans = ""; n = 0
}
$ awk '/=$/{sub(/=$/,""); printf "%s",$0;next} /./{print}' file
http://en.domain.com/registration.html#/?doitoken=1D7f1ad404-f84b-4a3b-8931-4f40b619730e
http://en.domain.com/registration.html#/?doitoken=5D8172f6e6-240f-42e6-8512-6d7f6bd61c2d
http://en.domain.com/registration.html#/?doitoken=8D8172f6e6-240f-42e6-8512-6d7f6bd61c2d
How it works:
/=$/{sub(/=$/,""); printf "%s",$0;next}
If the line ends with =, then remove the trailing =, print the result (without a trailing newline) and jump to the next line.
/./{print}
If we get here, then this line does not end with = and we just print it normally (with the trailing newline).

#awk If else loop not working in ksh

I have code where awk is piped to a clearcase command where If else loop is not working.
code is below :
#!/bin/ksh
export dst_region=$1
cleartool lsview -l | gawk -F":" \ '{ if ($0 ~ /Global path:/) { if($dst_region == "ABC" || $dst_region -eq "ABC") { system("echo dest_region is ABC");}
else { system("echo dest_region is not ABC"); } }; }'
But when I execute the above script the I get incorrect output,
*$ ksh script.sh ABCD
dest_region is ABC
$ ksh script.sh ABC
dest_region is ABC*
Could anyone please help on this issue ?
It would be useful if you explained exactly what you are trying to do but your awk script can be cleaned up a lot:
gawk -F":" -vdst_region="$1" '/Global path:/ { if (dst_region == "ABC") print "dest_region is ABC"; else print "dest_region is not ABC" }'
General points:
I have used -v to create an awk variable from the value of $1, the first argument to the script. This means that you can use it a lot more easily in the script.
awk's structure is condition { action } so you're using if around the whole one-liner unnecessarily
$0 ~ /Global path:/ can be changed to simply /Global path:/
the two sides of the || looked like they were trying to both do the same thing, so I got rid of the one that doesn't work in awk. Strings are compared using ==.
system("echo ...") is completely unnecessary. Use awk's built in print
You could go one step further and remove the if-else entirely:
gawk -F":" -vdst_region="$1" '/Global path:/ { printf "dest region is%s ABC", (dst_region=="ABC"?"":" not") }'

command substitution in bash with awk

Why does this work:
This
var=hello
myvar=`echo hello hi | awk "{ if (\\\$1 == \"$var\" ) print \\\$2; }"`
echo $myvar
gives
hi
But this does not?
This
var=hello
echo hello hi | awk "{ if (\\\$1 == \"$var\" ) print \\\$2; }"
gives
awk: cmd. line:1: Unexpected token
I am using
GNU bash, version 4.1.5(1)-release (i486-pc-linux-gnu)
on
Linux 2.6.32-34-generic-pae #77-Ubuntu SMP Tue Sep 13 21:16:18 UTC 2011 i686 GNU/Linux
The correct way to pass shell variables into an AWK program is to use AWK's variable passing feature instead of trying to embed the shell variable. And by using single quotes, you won't have to do a bunch of unnecessary escaping.
echo "hello hi" | awk -v var="$var" '{ if ($1 == var ) print $2; }'
Also, you should use $() instead of backticks.
If your awk is like mine, it will tell you where it fails:
var=hello
echo hello hi | awk "{ if (\\\$1 == \"$var\" ) print \\\$2; }"
awk: syntax error at source line 1
context is
{ if >>> (\ <<< $1 == "hello" ) print \$2; }
awk: illegal statement at source line 1
furthermore, if you replace awk by echo you'll see clearly why it fails
echo hello hi | echo "{ if (\\\$1 == \"$var\" ) print \\\$2; }"
{ if (\$1 == "hello" ) print \$2; }
there are extra '\' (backslashes) in the resulting command. This is because you removed the backquotes.
So the solutions is to remove a pair of \'s
echo hello hi | awk "{ if (\$1 == \"$var\" ) print \$2; }"
hi

awk save command ouput to variable

I need to execute a command per line of some file. For example:
file1.txt 100 4
file2.txt 19 8
So my awk script need to execute something like
command $1 $2 $3
and save the output of command $1 $2 $3, so system() will not work and neither will getline. (I can't pipe the output if I do something like this.)
The restriction to this problem is to use only awk. (i already had a solution with bashscriot + awk...but I only want awk...just to know more about this)
What's wrong with using getline?
$ ./test.awk test.txt
# ls -F | grep test
test.awk*
test.txt
# cat test.txt | nl
1 ls -F | grep test
2 cat test.txt | nl
3 cat test.awk
# cat test.awk
#!/usr/bin/awk -f
{
cmd[NR] = $0
while ($0 | getline line) output[NR] = output[NR] line RS
}
END {
for (i in cmd) print "# " cmd[i] ORS output[i]
}
Awk's system() function passes the string to /bin/sh, so you can use redirect operators, like ">file.out" if you want.
awk '{system("command " $1 " " $2 " " $3 ">" $1 ".out");}'
Edit: ok, by save, you mean into an awk variable. ephemient is on the right track, then. That's what awk's getline does, like backticks or $(cmd) in shell/perl. In fact, google for awk backticks found this:
http://www.omnigroup.com/mailman/archive/macosx-admin/2006-May/054665.html
You say you can't use getline because then you couldn't pipe. But you can work around that with tee and file-descriptor tricks. This works if /bin/sh is bash:
{ "set +o posix; command " $1 " " $2 " " $3 " | tee >(grep foo)" | getline var; print toupper(var); } # bash-only, and broken.
set +o posix is necessary because awk runs bash as sh, which makes it go into posix mode after readings its startup files. Hmm, I'm not having any luck getting that to work, and it requires bash anyway.
Ok, this works:
$ touch foo bar
$ echo "foo bar" |
awk '{ "{ ls " $1 " " $2 " " $3 " | tee /dev/fd/10 | grep foo > /dev/tty; } 10>&1" | getline var; print toupper(var); }'
foo
BAR

Resources