Grouping command substitution without double quotes? "$()" without the "" - bash

I'm writing a script that involves generating Awk programs and running them via awk $(...), as in
[lynko#hephaestus] ~ % awk $(echo 'BEGIN { print "hello!" }')
The generated program is going to be more complicated in the end, but first I want to make sure this is possible. In the past I've done
[lynko#hephaestus] ~ % program=$(echo 'BEGIN { print "hello" }')
[lynko#hephaestus] ~ % awk "$program"
hello!
where the grouping is unsurprising. But the first example (under GNU awk, which gives a more helpful error message than mawk which is default on my other machine) gives
[lynko#hephaestus] ~ % awk $(echo 'BEGIN { print "hello!" }')
awk: cmd. line:1: BEGIN blocks must have an action part
presumably because this is executed as awk BEGIN { print "hello!" } rather than awk 'BEGIN { print "hello!" }'. Is there a way I can force $(...) to remain as one group? I'd rather not use "$()" since I'd have to escape all the double-quotes in the program generator.
I'm running Bash 4.2.37 and mawk 1.3.3 on Crunchbang Waldorf.

Put quotes around it. You don't need to escape the double quotes inside it:
awk "$(echo 'BEGIN { print "hello!" }')"

I'm also wondering why you are using an echo statement. Awk doesn't need one.
awk 'BEGIN { print "Awk SQUAWK!" }'
That will work perfectly.

Related

Why awk if conditional matching is wrong

In my project, I have two files.
The content userid is :
6534
4524
4522
6635
The content userpwinfo.txt is:
nsgg315_RJ:x:4520:100::/home-gg/users/nsgg315_RJ:/bin/bash
nsgg316_ZJY:x:4521:100::/home-gg/users/nsgg316_ZJY:/bin/bash
nsgg317_CPA:x:4522:100::/home-gg/users/nsgg317_CPA:/bin/bash
nsgg318_ZRL:x:4523:100::/home-gg/users/nsgg318_ZRL:/bin/bash
nsgg319_YYM:x:4524:100::/home-gg/users/nsgg319_YYM:/bin/bash
Now I want to print the username which id is in userid. I writed a bash shell like:
for i in $(cat userid)
do
#username=`awk -F: '{if($3=="$i") print $1}' /root/userpwinfo.txt`
#username=`awk -F: '$3=="$i" {print $1}' /root/userpwinfo.txt`
#username=`awk -F: '{if($3~/$i/) print $1}' /root/userpwinfo.txt`
username=`awk -F: '{if($3==$i) print $1}' /root/userpwinfo.txt`
echo $username
done
But unlucky, it shows nothing. The correct result should be:
nsgg319_YYM
nsgg317_CPA
I have tried in command line:
awk -F: '{if($3==4524) print $1}' /root/userpwinfo.txt
It is OK
Maybe if($3==$i) is wrong in shell, Who can help me?
Your $i is the shell variable, but it's inside the quotation mark ' so awk will try to interpret it instead of the shell.
Try this:
username=`awk -F: '{if($3=='$i') print $1}' /root/userpwinfo.txt`
Note that the $i is between ' marks, meaning it's outside of the block that will be interpreted by awk, meaning it should be interpreted by the shell.
Also note that if you have an empty line in the input file, your awk command would be if($3==) which is invalid and will yield an error.
I'd like to comment also that awk is meant to have a filter and an execution block. You shouldn't need to write an if inside a block, unless you want something unusual. Meaning, your command would be more appropriately written as:
username=`awk -F: '($3=='$i'){print $1}' /root/userpwinfo.txt`
Note that even this is not a very good solution, but you already have much to think about with only these changes. When you're more familiar with awk or getting more professional, come back and check the comments. ;)
If username is what you needed using the 2 files, you could try
$ cat userpwinfo.txt
nsgg315_RJ:x:4520:100::/home-gg/users/nsgg315_RJ:/bin/bash
nsgg316_ZJY:x:4521:100::/home-gg/users/nsgg316_ZJY:/bin/bash
nsgg317_CPA:x:4522:100::/home-gg/users/nsgg317_CPA:/bin/bash
nsgg318_ZRL:x:4523:100::/home-gg/users/nsgg318_ZRL:/bin/bash
nsgg319_YYM:x:4524:100::/home-gg/users/nsgg319_YYM:/bin/bash
$ cat userid.txt
6534
4524
4522
6635
$ awk -F":" ' { if( NR==FNR ) { a[$3]=$1; next } ; if(a[$1]) print a[$1] }' userpwinfo.txt userid.txt
nsgg319_YYM
nsgg317_CPA

How do I add single quotes into my awk command output?

I’m using Mac Yosemite, bash shell. I want to output single quotes in my output, and I have tried several things, without success …
awk -F, '{OFS=",";print ''$1'',$4,$6,$7}' my_list.csv
and
awk -F, '{OFS=",";print "'"$1"'",$4,$6,$7}' my_list.csv
Neither of these adds the single quotes into my output. How can I achieve this?
using variable is one way to go:
kent$ echo "a b c"|awk -v q="'" '$1=q$1q'
'a' b c
or you pick the escaping way:
kent$ echo "a b c"|awk '$1="'\''"$1"'\''"'
'a' b c
I prefer the 1st one.
You can use octal code as well:
# octal code 047 for '
awk -F, -v OFS=, '{print "\047" $1 "\047", $4, $6, $7}' my_list.csv

AWK (unexpected newline or end of string)

I'm trying to execute bash script but I'm got stuck.
data
$ cat test.txt
cat,command,for
cp,command,for
ls,command,to
script
#!/bin/bash
CUT_FILE=test.txt
TRN_GUID="1bcd1adf-2016-443b-9f00-2e4ce20726d7"
LCTN_ID="8002"
LCTN_NAME="TEST FILE"
LCTN_ADDR1="This is test"
cat $CUT_FILE | awk -F ',' '{ print '$TRN_GUID','$LCTN_ID','$LCTN_NAME','$LCTN_ADDR1',$1,$2 }'
output
-bash-3.2# sh test4
awk: cmd. line:1: { print 1bcd1adf-2016-443b-9f00-2e4ce20726d7,8002,TEST
awk: cmd. line:1: ^ unexpected newline or end of string
desired output
1bcd1adf-2016-443b-9f00-2e4ce20726d7,8002,"TEST FILE","This is test",cat,command
Any ideas?
Syntax Error
You are closing your script using the single quotes.
awk -F ',' '{ print '$TRN_GUID','$LCTN_ID','$LCTN_NAME','$LCTN_ADDR1',$1,$2 }'
Can you see, using the syntax highlighting, that the script gets closed after print '? You should use double quotation marks inside your script to prevent this, like so:
awk -F ',' '{ print "$TRN_GUID", "$LCTN_ID", "$LCTN_NAME", "$LCTN_ADDR1", $1, $2 }'
Do you notice the difference in syntax highlighting?
Undesired output
You cannot access bash variables inside awk, to accomplish this you should pass them as variables. To do that, we simply use the -v option of awk. To illustrate this point:
awk -v trn_guid="$TRN_GUID" -v lctn_id="LCTN_ID" -v lctn_name="$LCTN_NAME" -v lctn_addr1="$LCTN_ADDR1" -v arg1="$1" -v arg2="$2" -F ',' '{ print trn_guid, lctn_id, lctn_name, lctn_addr1, arg1, arg2 }'
The syntax is simply -v variable="value". You can put anything in the value, including bash variables. The variable will be accessible in the awk script using the name you chose.
You don't need awk for this. Just use it like this in pure BASH:
CUT_FILE="test.txt"
TRN_GUID="1bcd1adf-2016-443b-9f00-2e4ce20726d7"
LCTN_ID="8002"
LCTN_NAME="TEST FILE"
LCTN_ADDR1="This is test"
while read -r line; do
echo "$TRN_GUID,$LCTN_ID,$LCTN_NAME,$LCTN_ADDR1,${line%,*}"
done < "$CUT_FILE"
Output:
1bcd1adf-2016-443b-9f00-2e4ce20726d7,8002,TEST FILE,This is test,cat,command
1bcd1adf-2016-443b-9f00-2e4ce20726d7,8002,TEST FILE,This is test,cp,command
1bcd1adf-2016-443b-9f00-2e4ce20726d7,8002,TEST FILE,This is test,ls,command

Having trouble with simple awk script

This works in the command line:
awk -F, '$5>1900 {print $2}' presidents.csv
But I am not sure how to do it in an awk script.
This is what I have tried so far in the script.
BEGIN {FS=","}
if ($5>1900)
{print $2}
The stuff you have in single quotes:
awk -F, '$5>1900 {print $2}' presidents.csv
Is an AWK script. Just stick that in a file:
BEGIN {FS=","}
$5>1900 {print $2}
AWK requires a pattern to search for and an action to be taken when it finds the pattern. Either pattern or action can be omitted but not both.
In your case first its a syntax mistake as you haven't enclosed your action within braces ( {} ) and so this will cause AWK to think both pattern and action got omitted. We can use relational expression as pattern in AWK and so instead of using an if statement you can use the relational expression as below.
$ cat test.awk
#!/usr/bin/awk -f
BEGIN {
FS=",";
}
$5 >= 1900 {
print $2;
}
AWk script would be like,
#!/usr/bin/awk
BEGIN {FS=",";}
{
if ($5>1900)
{print $2;}
}
That is, you need to enclose the if condition inside curly braces.
If you'd like a shell script that runs your awk command you can put the following into a file:
#!/bin/bash
awk -F, '$1>1900 {print $2}' $1
To make it so you can actually execute the script, you can run:
chmod +x ./script_file_name_here
Then you can run
./script_file_name_here presidents.csv
and have the same thing happen.

Use array variable in awk?

A=(aaa bbb ccc)
cat abc.txt | awk '{ print $1, ${A[$1]} }'
I want to index an array element based on the $1, but the code above is not correct in awk syntax. Could someone help?
You can't index a bash array using a value generated inside awk, even if you weren't using single quotes (thereby preventing bash from doing any substitution). You could pass the array in, though.
A=(aaa bbb ccc)
awk -v a="${A[*]}" 'BEGIN {split(a, A, / /)}
{print $1, A[$1] }' <abc.txt
Because of the split function inside awk, the elements of A may not contain spaces or newlines. If you need to do anything more interesting, set the array inside of awk.
awk 'BEGIN {a[1] = "foo bar" # sadly, there is no way to set an array all
a[2] = "baz" } # at once without abusing split() as above
{print $1, a[$1] }' <abc.txt
(Clarification: bash substitutes variables before invoking the program whose argument you're substituting, so by the time you have $1 in awk it's far too late to ask bash to use it to substitute a particular element of A.)
If you are going to be hard-coding the A array, you can just initialize it in awk
awk 'BEGIN{A[0]="aaa";A[1]="bbb"}{ print $1, A[$1] }' abc.txt
Your awk program within single quotes cannot see the shell environment variable A. In general, you can get a little shell substitution to work if you use double quotes instead of single quotes, but that is done by the shell, before awk is invoked. Overall, it is heavy sledding to try to combine shell and awk this way. If possible, I would take kurumi's approach of using an awk array.
Single quotes: an impenetrable veil.
Double quotes: generally too much travail.
So pick your poison: shell or awk.
Otherwise: your code may balk.
You can also print each element of the array on separate line with printf and pipe it to awk. This code will simply print bash array (bash_arr) from awk:
bash_arr=( 1 2 3 4 5 )
printf '%s\n' "${bash_arr[#]}" |
awk ' { awk_arr[NR] = $0 }
END {
for (key in awk_arr) {
print awk_arr[key]
}
}'

Resources