Escape dots in domains names using sed in Bash - bash

I am trying to keep the return of a sed substitution in a variable:
D=domain.com
echo $D | sed 's/\./\\./g'
Correctly returns: domain\.com
D1=`echo $D | sed 's/\./\\./g'`
echo $D1
Returns: domain.com
What am I doing wrong?

D2=`echo $D | sed 's/\./\\\\./g'`
echo $D2
Think of shells rescanning the line each time it is executed. Thus echo $D1, which has the escapes in it, have the escapes applied to the value as the line is parsed, before echo sees it. The solution is yet more escapes.
Getting the escapes correct on nested shell statements can make you live in interesting times.

The backtick operator replaces the escaped backslash by a backslash. You need to escape twice:
D1=`echo $D | sed 's/\./\\\\./g'`
You may also escape the first backslash if you like.

Related

Variable comparison and pattern replacement

I am trying to create a bash script that uses the sed command to replace a pattern by a variable that contains a string or put a space if there is nothing in the variable. I cannot find the good way to write it and make it work. Here is the part where I have issues:
a_flag=$(echo $a | wc -w)
if [[ $a_flag = 0 ]]; then
sed -i -e 's/#b/\\hspace{2cm}/g' source.tex
else
sed -i -e "s/#b/$a/g" source.tex
fi
When running this, the condition is always false. I tried [] or (()) for the if statement but I just can't find a way to fix it.
You only need a single parameter expansion here, to replace the expansion of $a with \hspace{2cm} if the expansion is empty.
sed -i -e "s/#b/${a:-\\\\hspace{2cm}}/g" source.tex
You need a stack of \ because there are two rounds of escaping involved. First, the shell itself reduces each \\ to a single literal backslash. Then sed also reduces each pair of \\ to a single literal backslash.
Counting the number of occurrences of someething seems like a very roundabout way to approach this anyway.
case $a in
*[A-Za-z0-9_]*) x='\\hspace{2cm}';;
*) x=$a;;
esac
sed -i "s/#b/$x/g" source.tex

double backslashes of sed single-quoted command inside command substitutions get translated to a single backslash

printf '%s' 'abc' | sed 's/./\\&/g' #1, \a\b\c
printf '%s' "`printf '%s' 'abc' | sed 's/./\\&/g'`" #2, &&&
The expression inside the second backticks returns \a\b\c, and we have printf '%s' "\a\b\c", so it should print \a\b\c.
My question is: why does the second script print &&& ?
note:
I can get the second script work (prints \a\b\c) by prepending each backslash with another backslash, but I don't know why it's needed.
One related question:
why does this single quoted string get interpreted when it's inside of a command substitution
This is a good example to show difference between back-tick and $(cmd) command substitutions.
When the old-style backquoted form of substitution is used, backslash
retains its literal meaning except when followed by "$", "`", or "\".
The first backticks not preceded by a backslash terminates the command
substitution. When using the "$(COMMAND)" form, all characters between
the parentheses make up the command; none are treated specially.
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html
So take a look your example, I used echo instead of printf:
kent$ echo 'abc' | sed 's/./\\&/g'
\a\b\c
kent$ echo -E "`echo 'abc' | sed 's/./\\&/g'`"
&&&
kent$ echo -E "$(echo 'abc' | sed 's/./\\&/g')"
\a\b\c
You can see, the back-tick command substitution made your \\ as single \, thus together with the followed & it became \& (literal &)
Note that I used echo -E in order to disable the interpretation of backslash escapes so that the \a\b\c could be printed out.
Because on the second line :
you are saying:
printf '%s' 'abc' -> 'abc'
Then replace:
'abc'| sed 's/./\\&g' -> &&&
The s mean substitute
. mean one character
\\& by a char &
g mean multiple occurrence on the line
So you are saying:
Replace in abc each char by & multiple time on the same line
Explanation of \\\& :
Two backslashes become a single backslash in the shell which then in sed escapes the forward slash which is the middle delimiter.
\\& -> \& (which makes the forward & a regular character instead of a delimiter)
Three of them: The first two become one in the shell which then escape the third one in sed
\\\& -> \\&
Finally! don't forget that you command is under backquote:
The reason you have to escape it "twice" is because you're entering this command in an environment (such as a shell script) that interprets the double-quoted string once. It then gets interpreted again by the subshell.
From:
Why does sed require 3 backslashes for a regular backslash?

Escaping backslash in AWK in command substituion

I am trying to escape backslash in AWK. This is a sample of what I am trying to do.
Say, I have a variable
$echo $a
hi
The following works
$echo $a | awk '{printf("\\\"%s\"",$1)'}
\"hi"
But, when I am trying to save the output of the same command to a variable using command substitution, I get the following error:
$ q=`echo $a | awk '{printf("\\\"%s\"",$1)'}`
awk: {printf("\\"%s\"",$1)}
awk: ^ backslash not last character on line
I am unable to understand why command substitution is breaking the AWK. Thanks a lot for your help.
Try this:
q=$(echo $a | awk '{printf("\\\"%s\"",$1)}')
Test:
$ a=hi
$ echo $a
hi
$ q=$(echo $a | awk '{printf("\\\"%s\"",$1)}')
$ echo $q
\"hi"
Update:
It will, it just gets a littler messier.
q=`echo $a | awk '{printf("\\\\\"%s\"",$1)}'`
Test:
$ b=hello
$ echo $b
hello
$ t=`echo $b | awk '{printf("\\\\\"%s\"",$1)}'`
$ echo $t
\"hello"
Reference
Quoting inside backquoted commands is somewhat complicated, mainy
because the same token is used to start and to end a backquoted
command. As a consequence, to nest backquoted commands, the backquotes
of the inner one have to be escaped using backslashes. Furthermore,
backslashes can be used to quote other backslashes and dollar signs
(the latter are in fact redundant). If the backquoted command is
contained within double quotes, a backslash can also be used to quote a
double quote. All these backslashes are removed when the shell reads
the backquoted command. All other backslashes are left intact.
The new $(...) avoids these troubles.
Don't get into bad habits with backticks, quoting and parsing shell variables to awk The correct way to do this is:
$ shell_var="hi"
$ awk -v awk_var="$shell_var" -v c='\' 'BEGIN{printf "%s%s\n",c,awk_var}'
\hi
$ res=$(awk -v awk_var="$shell_var" -v c='\' 'BEGIN{printf "%s%s\n",c,awk_var}')
$ echo "$res"
\hi

sed won't perform the required subs inside shell script

In the below shell script I try to print A2D(Vlog-Ams-#Cross) with special characters escaped. For example replace ( with \( but sed won't have any effect.
#! /bin/sh
parameter="A2D(Vlog-Ams-#Cross)"
echo $parameter
parameterz=`echo "$parameter" | sed 's/(/\\(/g'`
echo $parameterz
The output is
A2D(Vlog-Ams-#Cross)
A2D(Vlog-Ams-#Cross)
If I do the same on my c-shell terminal, it works fine.
Any ideas?
You use backslashs within a backtick command and that's tricky. If the sed command didn't occur within backticks, it would work correctly. When the shell looks for the closing backtick, however, it removes one level of backslash quoting, so you get
sed 's/(/\(/g'
and that's a no-op. If your shell permits it, use $(...) instead of backticks; in this way you avoid these quoting problems.
In your replacement \\( the first \ escapes the second \. But you must escape the (, too:
$ echo 'A2D(Vlog-Ams-#Cross)' | sed -e 's/(/\\\(/g' -e 's/)/\\\)/g'
A2D\(Vlog-Ams-#Cross\)

Search for single quoted grep strings?

I have a string, "$server['fish_stick']" (disregard double quotes)
I don't know how to successfully grep for an exact match for this string. I've tried many ways.
I've tried,
rgrep -i \$'server'\[\''fish'\_'stick'\'\] .
rgrep -i "\$server\[\'fish\_stick\'\]" .
rgrep -i '\$server\[\'fish\_stick\'\]' .
Is it single quotes that are causing my issue?
When I echo the first grep out it shows exactly what I want to search but returns garbage results like anything with $server in it.
Please help and explain, thank you!
The main problem here is that you are not quoting the argument being passed to grep. The only thing that needs to be escaped is \$ (if double quoted) and []. If you want the exact string (not using regex), just use fgrep (grep -F) which does exact string matching:
grep -F "\$server['fish_stick']"
Works on my system:
$ foo="\$server['fish_stick']"
$ echo "$foo" | grep -F "\$server['fish_stick']"
$server['fish_stick']
Using regex:
$ echo "$foo" | grep "\$server\['fish_stick'\]"
$server['fish_stick']
Using regex and handling nested single quotes:
$ echo "$foo" | grep '\$server\['\''fish_stick'\''\]'
$server['fish_stick']
Inside of single quotes, nested single quotes can not be not be escaped. You have to close the quotes, and then reopen it to "escape" the single quotes.
http://mywiki.wooledge.org/Quotes
I don't suppose you're asking how to get that string into a variable without having quoting issues. If you are, here's a way using a here-document:
str=$(cat <<'END'
$foo['bar']
END
)
To address your concern about escaping special characters for grep, you could use sed to put a backslash before any non-alphanumeric character:
grep "$(sed 's/[^[:alnum:]]/\\&/g' <<< "$str")" ...
When used with set -x, the grep command looks like: grep '\$foo\[\'\''bar\'\''\]' ...

Resources