This question already has answers here:
How do I use shell variables in an awk script?
(7 answers)
Closed 4 years ago.
I'm trying to pass variable in loop with awk command to find values. I have a file:
input.txt
1234|something|ohmygod
2345|urabura|kangura
9999|1234|xxxsecrets
shell command
cat input.txt | awk -F'|' '$1 ~ /1234/'
or
awk -F'|' '$1 ~ /1234/' input.txt
get first line from file as desired. Problem occurs when I try to print this via bash. When I simply test echo like:
echo `cat input.txt | awk -F'|' '$1 ~ /1234/'`
or
echo `awk -F'|' '$1 ~ /1234/' input`
I got desired output, but unfortunately when I try to pass variable inside it
variable1="1234"
echo `awk -F'|' '$1 ~ /"$variable1"/' input`
or
variable1="1234"
echo `awk -v var="$variable1" -F'|' '$1 ~ /var/' input`
it gives one empty line. Please suggest how to pass variable inside regex awk filter.
PS It is not duplicate question to: How do I use shell variables in an awk script? due to fact that I have knowledge how to use variable in AWK as I posted up here (-v parameter) but the question is how to PASS variable in REGEX in AWK (place between two slashes - echo awk -F'|' '$1 ~ /"$variable"/' input)
What you asked for:
awk -v var="$variable1" -F'|' '$1 ~ var' input
What you actually need:
awk -v var="$variable1" -F'|' '$1 == var' input
See http://cfajohnson.com/shell/cus-faq-2.html#Q24
Related
I found some ways to pass external shell variables to an awk script, but I'm confused about ' and ".
First, I tried with a shell script:
$ v=123test
$ echo $v
123test
$ echo "$v"
123test
Then tried awk:
$ awk 'BEGIN{print "'$v'"}'
$ 123test
$ awk 'BEGIN{print '"$v"'}'
$ 123
Why is the difference?
Lastly I tried this:
$ awk 'BEGIN{print " '$v' "}'
$ 123test
$ awk 'BEGIN{print ' "$v" '}'
awk: cmd. line:1: BEGIN{print
awk: cmd. line:1: ^ unexpected newline or end of string
I'm confused about this.
#Getting shell variables into awk
may be done in several ways. Some are better than others. This should cover most of them. If you have a comment, please leave below. v1.5
Using -v (The best way, most portable)
Use the -v option: (P.S. use a space after -v or it will be less portable. E.g., awk -v var= not awk -vvar=)
variable="line one\nline two"
awk -v var="$variable" 'BEGIN {print var}'
line one
line two
This should be compatible with most awk, and the variable is available in the BEGIN block as well:
If you have multiple variables:
awk -v a="$var1" -v b="$var2" 'BEGIN {print a,b}'
Warning. As Ed Morton writes, escape sequences will be interpreted so \t becomes a real tab and not \t if that is what you search for. Can be solved by using ENVIRON[] or access it via ARGV[]
PS If you have vertical bar or other regexp meta characters as separator like |?( etc, they must be double escaped. Example 3 vertical bars ||| becomes -F'\\|\\|\\|'. You can also use -F"[|][|][|]".
Example on getting data from a program/function inn to awk (here date is used)
awk -v time="$(date +"%F %H:%M" -d '-1 minute')" 'BEGIN {print time}'
Example of testing the contents of a shell variable as a regexp:
awk -v var="$variable" '$0 ~ var{print "found it"}'
Variable after code block
Here we get the variable after the awk code. This will work fine as long as you do not need the variable in the BEGIN block:
variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
Adding multiple variables:
awk '{print a,b,$0}' a="$var1" b="$var2" file
In this way we can also set different Field Separator FS for each file.
awk 'some code' FS=',' file1.txt FS=';' file2.ext
Variable after the code block will not work for the BEGIN block:
echo "input data" | awk 'BEGIN {print var}' var="${variable}"
Here-string
Variable can also be added to awk using a here-string from shells that support them (including Bash):
awk '{print $0}' <<< "$variable"
test
This is the same as:
printf '%s' "$variable" | awk '{print $0}'
P.S. this treats the variable as a file input.
ENVIRON input
As TrueY writes, you can use the ENVIRON to print Environment Variables.
Setting a variable before running AWK, you can print it out like this:
X=MyVar
awk 'BEGIN{print ENVIRON["X"],ENVIRON["SHELL"]}'
MyVar /bin/bash
ARGV input
As Steven Penny writes, you can use ARGV to get the data into awk:
v="my data"
awk 'BEGIN {print ARGV[1]}' "$v"
my data
To get the data into the code itself, not just the BEGIN:
v="my data"
echo "test" | awk 'BEGIN{var=ARGV[1];ARGV[1]=""} {print var, $0}' "$v"
my data test
Variable within the code: USE WITH CAUTION
You can use a variable within the awk code, but it's messy and hard to read, and as Charles Duffy points out, this version may also be a victim of code injection. If someone adds bad stuff to the variable, it will be executed as part of the awk code.
This works by extracting the variable within the code, so it becomes a part of it.
If you want to make an awk that changes dynamically with use of variables, you can do it this way, but DO NOT use it for normal variables.
variable="line one\nline two"
awk 'BEGIN {print "'"$variable"'"}'
line one
line two
Here is an example of code injection:
variable='line one\nline two" ; for (i=1;i<=1000;++i) print i"'
awk 'BEGIN {print "'"$variable"'"}'
line one
line two
1
2
3
.
.
1000
You can add lots of commands to awk this way. Even make it crash with non valid commands.
One valid use of this approach, though, is when you want to pass a symbol to awk to be applied to some input, e.g. a simple calculator:
$ calc() { awk -v x="$1" -v z="$3" 'BEGIN{ print x '"$2"' z }'; }
$ calc 2.7 '+' 3.4
6.1
$ calc 2.7 '*' 3.4
9.18
There is no way to do that using an awk variable populated with the value of a shell variable, you NEED the shell variable to expand to become part of the text of the awk script before awk interprets it. (see comment below by Ed M.)
Extra info:
Use of double quote
It's always good to double quote variable "$variable"
If not, multiple lines will be added as a long single line.
Example:
var="Line one
This is line two"
echo $var
Line one This is line two
echo "$var"
Line one
This is line two
Other errors you can get without double quote:
variable="line one\nline two"
awk -v var=$variable 'BEGIN {print var}'
awk: cmd. line:1: one\nline
awk: cmd. line:1: ^ backslash not last character on line
awk: cmd. line:1: one\nline
awk: cmd. line:1: ^ syntax error
And with single quote, it does not expand the value of the variable:
awk -v var='$variable' 'BEGIN {print var}'
$variable
More info about AWK and variables
Read this faq.
It seems that the good-old ENVIRON awk built-in hash is not mentioned at all. An example of its usage:
$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt
You could pass in the command-line option -v with a variable name (v) and a value (=) of the environment variable ("${v}"):
% awk -vv="${v}" 'BEGIN { print v }'
123test
Or to make it clearer (with far fewer vs):
% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test
You can utilize ARGV:
v=123test
awk 'BEGIN {print ARGV[1]}' "$v"
Note that if you are going to continue into the body, you will need to adjust
ARGC:
awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"
I just changed #Jotne's answer for "for loop".
for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done
I had to insert date at the beginning of the lines of a log file and it's done like below:
DATE=$(date +"%Y-%m-%d")
awk '{ print "'"$DATE"'", $0; }' /path_to_log_file/log_file.log
It can be redirect to another file to save
Pro Tip
It could come handy to create a function that handles this so you dont have to type everything every time. Using the selected solution we get...
awk_switch_columns() {
cat < /dev/stdin | awk -v a="$1" -v b="$2" " { t = \$a; \$a = \$b; \$b = t; print; } "
}
And use it as...
echo 'a b c d' | awk_switch_columns 2 4
Output:
a d c b
I'm trying to find (with awk) the IP of a specific ethernet interface using the hostname as a search patter (suffixed by the name of the ethernet interface). I wrote this little script but it outputs nothing and I don't understand why...
#!/bin/bash
name=$(hostname -s)-eth3
IP1=`awk -v var=$name '/var/ {print $1}' /etc/hosts`
echo $IP1
Could you please make few changes as shown following, which may help you.
#!/bin/bash
name=$(hostname -s)-eth3
IP1=$(awk -v var=$name '$0 ~ var{print $1}' "/etc/hosts")
echo "$IP1"
changes like backtick is not encouraged for storing values in bash variables $ should be used and use echo "$var" too.
Inside slashes, awk treats var as a literal string, not a variable.
Thus, replace:
/var/
With:
$0 ~ var
Thus use:
#!/bin/bash
name=$(hostname -s)-eth3
ip1=`awk -v var=$name '$0 ~ var {print $1}' /etc/hosts`
echo "$ip1"
Example
The first awk script below produces no match but the second does:
$ echo host-eth1 | awk -v var='host-eth1' '/var/ {print $1}'
$ echo host-eth1 | awk -v var='host-eth1' '$0 ~ var {print $1}'
host-eth1
I have a little test file containing:
awk this
and not awk this
but awk this
so do awk this
And I've tried the following awk commands, in bash, but each produces no output:
f=awk; awk '/$f/ && !/not/' test.txt
f=awk; awk '/\$f/ && !/not/' test.txt
f=awk; awk '/"$f"/ && !/not/' test.txt
f=awk; awk -v f="$f" '/f/ && !/not/' gtest.txt
Using double quotes " produces "event not found" error in the shell due to the !.
How can I search on a variable and negate another string in the same command?
Use awk like this:
f='awk'
awk -v f="$f" -v n='not' '$0 ~ f && $0 !~ n' file
awk this
but awk this
so do awk this
Or if you don't want to pass n='not' to awk:
awk -v f="$f" '$0 ~ f && $0 !~ /not/' file
awk this
but awk this
so do awk this
awk points to gawk for me and the following worked just fine:
awk -vf=awk '$0 ~ f && !/not/' file
I spent on this 2 hours and get nothing. I want to get $1 and $2 as a first command line input of shell script, but I couldn't manage this. And $3 and $0 would be columns in awk. I try different methods but nothing works for me.
awk -F':' -v "limit=1000" '{ if ( $3 >=limit ) gsub("~/$1/",~/$2/); print \$0}' file.txt
the cleanest method is to explicitly pass the values from shell to awk with awk's -v option:
awk -F: -v limit=1000 -v patt="~/$1/" -v repl="~/$2/" '
$3 >=limit {gsub(patt,repl); print}
' file.txt
When your awk line is part of a script file, and you want to use $1 and $2 from the script in your awk command, you should temporary stop the literal string with a single quote and start it again.
awk -F':' -v "limit=1000" '{ if ( $3 >=limit ) gsub("~/'$1'/",~/'$2'/); print $0}' file.txt
You didn't post any sample input or expected output so this is a guess but you probably want something like this:
awk -F':' -v limit=1000 -v arg1="$1" -v arg2="$2" '$3 >= limit{gsub("~/" arg1 "/","~/" arg2 "/"); print}' file.txt
I have to find all of the record which have a particular data which I am gonna pass as the command line argument.
awk expression is like this(Date is in this format :'02/08/2013')
cat records.txt| awk -F ',' '$4 ~ /02/08/2013/ {print $1 $2}'
Here 4th column is the date column.
What I want to do is that, provide the date as the first argument and compare it.
I tried this,But it is not working.
cat records.txt| awk -F ',' -v awkvar="$1" '$4 ~ /^"awkvar/ {print $1 $2}'
Here the date column starts with " quote, so I am telling to look for the records who start with "+awkvar the given date.
Can anyone help me with this?
Edit:
awk -F ',' -v var1="$1" '$4 ~ /^"2013/ {print $1 $2}' {This one is working, as I am directly comparing the record with 2013}
when I do this
awk -F ',' -v var1="$1" '$4 ~ /^"var1/ {print $1 $2}' , it does not return anything, what is the difference.
To pass variables to awk, use
awk -v awkvar=$value '{print awkvar}'
That said, no need to pipe cat | awk (useless use of cat) so finally :
awk -F, -v awkvar="$1" '$4 ~ "^\""awkvar {print $1 $2}' records.txt