Why can't I escape quote in gawk? - linux

I'm trying to do the following, but either I'm way too tired and can't think, or something weird is hapening with the escapes:
scanimage -L | gawk '/N650U/ {print gensub("[\'`]", "", "g", $2)}'
pipe bquote>

The idiom to do this is to create a variable which contains the single quote and then use that:
scanimage -L | gawk '/N650U/ {print gensub(q"`", "", "g", $2)}' q="'"
However, since you are using it in a character class, that is not going to work so you'll need to do this:
scanimage -L | gawk '/N650U/ {print gensub("[`'\'']", "", "g", $2)}'
<-- 1st pair --> <-- 2nd pair -->
Another alternative if using bash is to use $'' which does support escaping single-quotes
scanimage -L | gawk $'/N650U/ {print gensub("[`\']", "", "g", $2)}'
All you are doing in the 2nd case is creating a single-quote pair right before your literal single-quote, escaping the single quote so the shell doesn't interpret it and then make another single-quote pair after it.
Example with single-quote in a regex
$ echo $'foo`\'' | awk '{gsub(/[o`'\'']/,"#")}1'
f####
Example with single-quote outside a regex
$ echo "foo" | awk '{print q$0q}' q="'"
'foo'
Example with single-quote inside $''
echo $'foo`\'' | awk $'{gsub(/[o`\']/,"#")}1'
f####

There's no special character in single quotes including backslash(\).
Enclosing characters in single quotes (') preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash.
You can change the command to:
$ scanimage -L | awk '/N650U/ {print gensub("['"'"'`]", "", "g", $2)}'

Shell '...' doesn't support backslash escapes. You'll have to use "..." instead, I'm afraid.
gawk "/N650U/ {print gensub(\"['`]\", \"\", \"g\", \$2)}\"
(Note that shell "..." does expand $ variables, so you need to escape that as well!)

gawk '/N650U/ {print gensub("[\'`]", "", "g", $2)}'
^ ^
beginning of sq string end of single quoted string
A bit ugly, but this works:
gawk '/N650U/ {print gensub("['"'"'`]", "", "g", $2)}'

The correct way to do this is simply:
scanimage -L | gawk '/N650U/ {print gensub(/[\047`]/, "", "g", $2)}'
It doesn't affect using \047 for a single quote but as an aside given all of the other posted solutions are using string delimiters, note the use of regexp delimiters instead of string delimiters around the regexp that is gsub()s first argument. It matters, see for example:
$ printf 'a\\tb\n' | awk '{sub(/\\t/,"X")}1'
aXb
$ printf 'a\\tb\n' | awk '{sub("\\t","X")}1'
a\tb
$ printf 'a\\tb\n' | awk '{sub("\\\\t","X")}1'
aXb
The only time to use string delimiters in a regexp context is when you need to concatenate a literal with a variable, e.g.:
awk '{sub("<"var">","")}1'
Look up string and regexp delimiters in the man page for details.

Related

bash eval command with two double quotes [duplicate]

I am trying to assign a variable in bash:
assignssid="airport -I | awk '/ SSID/ {print substr($0, index($0, $2))}'"
When I currently do this, and then run echo "$assignssid", the result is something like the following:
airport -I | awk '/ SSID/ {print substr(-bash, index(-bash, ))}'
How do I get the variable to work with the code inside the quotes?
The Problem: Single Quotes Aren't Syntactic Within Double Quotes
When you have "'$0'", only those outer quotes are syntactic (and control how the shell expands the value: The inner quotes are simply literal values, and don't change how $0 is treated.
Literal Answer (bash-only): $''
If you don't want expansions to be honored, don't use double quotes.
# assign the directory separately, just to make the line shorter for easier reading
dir=/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/
# now, expand the directory name in double quotes, but use the $'' quoting style otherwise
assignssid="$dir"$'airport -I | awk \'/ SSID/ {print substr($0, index($0, $2))}\''
Inside $'', \' is a literal single quote (likewise, \n is a newline, \t is a tab, etc), and no parameter expansions take place ($'$0' is the same as '$0', not "$0").
After doing this, you can see that echoing the variable emits your command with single-quoted contents intact:
$ echo "$assignssid"
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | awk '/ SSID/ {print substr($0, index($0, $2))}'
...or, more germane to your presumably-intended use case:
current_ssid=$(eval "$assignssid")
Literal Answer (POSIX sh): '"'"'
Let's do the first answer in a way that isn't bash-only:
dir=/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/
assignssid="$dir"'airport -I | awk '"'"'/ SSID/ {print substr($0, index($0, $2))}'"'"''
What this string does is as follows:
The first ' closes the single-quoted context
The first " opens a double-quoted context
The following ' is literal, thus becomes part of your value
The following " closes the double-quoted context
The final ' resumes the original single-quoted context
Best-Practice Answer: Use Functions To Store Code
A string variable isn't the appropriate tool for this job at all. Instead, use a function:
airport_dir=/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/
assignssid() {
"$airport_dir"/airport -I | awk '/ SSID/ {print substr($0, index($0, $2))}'
}
...and thereafter:
current_ssid=$(assignssid)
You could "divide and conquer".
Lets put the (very) long directory in one variable:
$ dir="/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources"
Lets put the awk command in a second variable:
$ awkcmd='/ SSID/ {print substr($0, index($0, $2))}'
As the text is inside single quotes, the $0 and $2 are not expanded by the shell.
And, lets join all in the variable you want:
$ assignssid="$dir/airport -I | awk '$awkcmd'"
The single quotes work (appear in the assignssid variable) in this case because they are being used inside the double quotes.
And, I'll assume that what you want to execute is:
$ eval "$assignssid"
The above will work in any (reasonable) shell.
Thanks all for your contributions. I think I found a different way to do it
var=$(/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | awk '/ SSID/ {print substr($0, index($0, $2))}')

BASH shell execution from string with positional parameters

When I try to run the code below, the shell is replacing (because they are not defined as a bash variable) $4 and $2 with blanks. My question is, how do I keep bash from trying to evaluate the positional parameters for awk as its variables?
I've tried putting double and single quotes around the positional parameters, however, that did not suppress bash from interpreting them as local variables instead of strings.
This is what is returned when I echo "$x$i$y"
date -r /root/capture/capture11.mp4 | awk '{print }' | awk -F":" '/1/ {print }'
Code:
#!/bin/sh
i=$(cat /etc/hour.conf)
x="date -r /root/capture/capture"
y=".mp4 | awk '{print $4}' | awk -F\":\" '/1/ {print $2}'"
$x$i$y
Any help would be greatly appreciated!
Variables are interpolated inside double quotes. Use single quotes, or escape them like \$2.
However, the way you're trying to split up the command into separate variables won't work. Instead, you should use a function. Then you don't need to deal with quotes and escaping at all. For instance:
do_thing() {
date -r "/root/capture/capture$1.mp4" | awk '{print $4}' | awk -F':' '/1/ {print $2}'
}
do_thing "$(cat /etc/hour.conf)"
$4 is doubled quoted. Though there are single quotes, it is included in double quotes. So the single quotes are just part of the string and it won't keep the literal meaning of $. So you can escape the $:
y=".mp4 | awk '{print \$4}' | awk -F\":\" '/1/ {print \$2}'"
Or, use single quotes around the whole part:
y='.mp4 | awk "{print \$4}" | awk -F':' "/1/ {print \$2}"'
Concatenating variables like that to build a command line sort of works, but quotes within the variables don't quote anything, they'll just be taken as literal quotes.
This sort of works (but is horrible):
$ x=prin; y="tf %f\\n"; z=" 123456789"
$ $x$y$z
123456789.000000
This doesn't do what you want:
$ z='"foo bar"'; printf $y ; echo
"foo
Instead of one argument foo bar, printf gets the two arguments "foo and bar".

Field separation with adding quotes

I am beginner in shell script .
I have one variable containing value having = character.
I want to add quote in fields after = Character.
abc="source=TDG"
echo $abc|awk -F"=" '{print $2}'
My code is printing one field only.
my expected output is
source='TDG'
$ abc='source=TDG'
$ echo "$abc" | sed 's/[^=]*$/\x27&\x27/'
source='TDG'
[^=]*$ match non = characters at end of line
\x27&\x27 add single quotes around the matched text
With awk
$ echo "$abc" | awk -F= '{print $1 FS "\047" $2 "\047"}'
source='TDG'
-F= input field separator is =
print $1 FS "\047" $2 "\047" print first field, followed by input field separator, followed by single quotes then second field and another single quotes
See how to escape single quote in awk inside printf
for more ways of handling single quotes in print
With bash parameter expansion
$ echo "${abc%=*}='${abc#*=}'"
source='TDG'
${abc%=*} will delete last occurrence of = and zero or more characters after it
${abc#*=} will delete zero or more characters and first = from start of string
Sed would be the better choice:
echo "$abc" | sed "s/[^=]*$/'&'/"
Awk can do it but needs extra bits:
echo "$abc" | awk -F= 'gsub(/(^|$)/,"\047",$2)' OFS==
What is taking place?
Using sub to surround TDG with single quotes by its octal nr to avoid quoting problems.
echo "$abc" | awk '{sub(/TDG/,"\047TDG\047")}1'
source='TDG'

awk print value without quote sign

I have this value
option 'staticip' '5.5.5.1'
I want to print only 5.5.5.1 without quote sign. I have use
cat /etc/filename | grep staticip | awk '{print $3}'
but the result come with '5.5.5.1'
Or, you can use tr to remove the offending characters:
cat /etc/filename | grep staticip | awk '{print $3}' | tr -d \'
You can use awk's gsub() function to change the quotes to nothing.
awk '{gsub(/'"'"'/, "", $3); print $3}'
Note this is really gsub(/'/, "", $3). The ugliness comes from the need to glue quotes together.
awk '$2=="staticip" && $0=$4' FS="'"
Result
5.5.5.1
To remove the ' from the awk output you can use
sed "s/^'//;s/'$//"
This command removes the ' only at the beginning and the end of the output line and is not so heavy as to use awk and not so general if using tr.
awk is much bgiger in memory and tr removes all ' from the output what is not always intended.
You could use awks substr function or pipe that to the cut command. I leave you to read the man page for awk substr.

Remove a specific character using awk or sed

I have a command output from which I want to remove the double quotes ".
Command:
strings -a libAddressDoctor5.so |\
grep EngineVersion |\
awk '{if(NR==2)print}' |\
awk '{print$2}'
Output:
EngineVersion="5.2.5.624"
I'd like to know how to remove unwanted characters with awk or sed.
Use sed's substitution: sed 's/"//g'
s/X/Y/ replaces X with Y.
g means all occurrences should be replaced, not just the first one.
Using just awk you could do (I also shortened some of your piping):
strings -a libAddressDoctor5.so | awk '/EngineVersion/ { if(NR==2) { gsub("\"",""); print $2 } }'
I can't verify it for you because I don't know your exact input, but the following works:
echo "Blah EngineVersion=\"123\"" | awk '/EngineVersion/ { gsub("\"",""); print $2 }'
See also this question on removing single quotes.
tr can be more concise for removing characters than sed or awk, especially when you want to remove multiple different characters from a string.
Removing double quotes:
echo '"Hi"' | tr -d \"
# Prints Hi without quotes
Removing different kinds of brackets:
echo '[{Hi}]' | tr -d {}[]
# Prints Hi without brackets
-d stands for "delete".

Resources