How to escape characters from a single command? - bash

How do I escape characters in linux using the sed command?
I want to print something like this
echo hey$ya
But I'm just receiving a
hey
how can escape the $ character?

The reason you are only seing "hey" echoed is that because of the $, the shell tries to expand a variable called ya. Since no such variable exists, it expands to an empty string (basically it disappears).
You can use single quotes, they prevent variable expansion :
echo 'hey$ya'
You can also escape the character :
echo hey\$ya
Strings can also be enclosed in double quotes (e.g. echo "hey$ya"), but these do not prevent expansion, all they do is keep the whole expression as a single string instead of allowing word splitting to separate words in separate arguments for the command being executed. Using double quotes would not work in your case.

\ is the escape character. So your example would be:
~ » echo hey\$ya
hey$ya
~ »

Related

Trouble understanding the non-obvious use of backslash inside of backticks

I have read a ton of pages including the bash manual, but still find the "non-obvious" use of backslashes confusing.
If I do:
echo \*
it prints a single asterisks, this is normal as I am escaping the asterisks making it literal.
If I do:
echo \\*
it prints \*
This also seems normal, the first backslash escapes the second.
If I do
echo `echo \\*`
It prints the contents of the directory. But in my mind it should print the same as echo \\* because when that is substituted and passed to echo. I understand this is the non-obvious use of backslashes everyone talks about, but I am struggling to understand WHY it happens.
Also the bash manual says
When the old-style backquote form of substitution is used, backslash retains its literal meaning except when followed by ‘$’, ‘`’, or ‘\’.
But it doesn't define what the "literal meaning on backslash" is. Is it as an escape character, a continuation character, or just literally a backslash character?
Also, it says it retain it's literal meaning, except when followed by ... So when it's followed by one of those three characters what does it do? Does it only escape those three characters?
This is mostly for historical interest since `...` command substitution has been superseded by the cleaner $(...) form. No new script should ever use backticks.
Here's how you evaluate a $(command) substitution
Run the command
Here's how you evaluate a `string` command substitution:
Determine the span of the string, from the opening backtick to the closing unescaped backtick (behavior is undefined if this backtick is inside a string literal: the shell will typically either treat it as literal backtick or as a closing backtick depending on its parser implementation)
Unescape the string by removing backslashes that come before one of the three characters dollar, backtick or backslash. This following character is then inserted literally into the command. A backslash followed by any other character will be left alone.
E.g. Hello\\ World will become Hello\ World, because the \\ is replaced with \
Hello\ World will also become Hello\ World, because the backslash is followed by a character other than one of those three, and therefore retains its literal meaning of just being a backslash
\\\* will become \\* since the \\ will become just \ (since backslash is one of the three), and the \* will remain \* (since asterisk is not)
Evaluate the result as a shell command (this includes following all regular shell escaping rules on the result of the now-unescaped command string)
So to evaluate echo `echo \\*`:
Determine the span of the string, here echo \\*
Unescape it according to the backtick quoting rules: echo \*
Evaluate it as a command, which runs echo to output a literal *
Since the result of the substitution is unquoted, the output will undergo:
Word splitting: * becomes * (since it's just one word)
Pathname expansion on each of the words, so * becomes bin Desktop Downloads Photos public_html according to files in the current directory
Note in particular that this was not the same as replacing the the backtick command with the output and rerunning the result. For example, we did not consider escapes, quotes and expansions in the output, which a simple text based macro expansion would have.
Pass each of these as arguments to the next command (also echo): echo bin Desktop Downloads Photos public_html
The result is a list of files in the current directory.

b2 command finds ICU when called directly but not from indirect bash script variable

I have this strange issue with my bash script. I compile boost as part of it. The call from the script looks like this:
./b2 --reconfigure ${PARALLEL} link=static cxxflags=-fPIC install boost.locale.iconv=off boost.locale.posix=off -sICU_PATH="${ICU_PREFIX}" -sICU_LINK="${BOOST_ICU_LIBS}" >> "${BOOST_LOG}" 2>&1
That command works perfectly well. The log file shows that it finds ICU without a problem. However, if I change it to run from a variable, it no longer finds ICU (but it still compiles everything else):
bcmd="./b2 --reconfigure ${PARALLEL} link=static cxxflags=-fPIC install boost.locale.iconv=off boost.locale.posix=off -sICU_PATH=\"${ICU_PREFIX}\" -sICU_LINK=\"${BOOST_ICU_LIBS}\""
$bcmd >> "${BOOST_LOG}" 2>&1
What's the difference? I would like to be able to use the second approach so that I can pass the command into another function before running it.
Don't use a variable to store complex commands involving quotes that are nested. The problem is when you call the variable with just $cmd, the quotes are stripped incorrectly. Putting commands (or parts of commands) into variables and then getting them back out intact is complicated.
Quote removal is part of the one of the word expansions done by the shell. From the excerpt seen in POSIX specification of shell
2.6.7 Quote Removal
The quote characters ( backslash, single-quote, and double-quote) that were present in the original word shall be removed unless they have themselves been quoted.
Your example can be simply reproduced by a simple example. Assuming you have a few command flags (not actual ones)
cmdFlags='--archive --exclude="foo bar.txt"'
If you carefully look through the above, it contains 2 args, one --archive and another for --exclude="foo bar.txt", notice the double-quotes which needs to be preserved when you are passing it.
Notice how the quotes are incorrectly split when I don't quote cmdFlags, in the printf() call below
printf "'%s' " $cmdFlags; printf '\n'
'--archive' '--exclude="foo' 'bar.txt"'
and compare the result with one with proper quoting done below.
printf "'%s' " "$cmdFlags"; printf '\n'
'--archive --exclude="foo bar.txt"'
So along with the suggestion of properly quoting the variable, the general suggestion would be to use an array to store the flags and pass the quoted array expansion
cmdArray=()
cmdArray=(./b2 --reconfigure ${PARALLEL} link=static cxxflags=-fPIC install boost.locale.iconv=off boost.locale.posix=off -sICU_PATH="${ICU_PREFIX}" -sICU_LINK="${BOOST_ICU_LIBS}")
and pass the array as
"${cmdArrray[#]}" >> "${BOOST_LOG}" 2>&1
Try to use eval when you want to execute a string as a command. This way, you won't have issues regarding strings that have spaces etc. The expanded cmd string is not re-evaluated by bash hence, things like "hi there" are expanded as two separate tokens.
eval "$bcmd" >> "${BOOST_LOG}" 2>&1
To demonstrate this behavior, consider this code:
cmd='echo "hi there"'
$cmd
eval "$cmd"
Which outputs to:
"hi there"
hi there
The token "hi there" is not re-evaluated as a quoted string.
Use single quota instead of two.
bcmd='./b2 --reconfigure ${PARALLEL} link=static cxxflags=-fPIC install boost.locale.iconv=off boost.locale.posix=off -sICU_PATH=\"${ICU_PREFIX}\" -sICU_LINK=\"${BOOST_ICU_LIBS}\"'
Bash documentation states that single quota does not interpolate:
3.1.2.2 Single Quotes
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.
That way you omit double-quota stripping and removing problem, and your string will be passed correctly. If you want to stay with double quotes, you have to vary that they DO NOT preserve literal value of certain characters: $, ' and \ unless preceded with \, as manual states:
3.1.2.3 Double Quotes
Enclosing characters in double quotes (") preserves the literal value of all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !. The characters $ and ` retain their special meaning within double quotes (see Shell Expansions). The backslash retains its special meaning only when followed by one of the following characters: $, `, ", \, or newline. Within double quotes, backslashes that are followed by one of these characters are removed. Backslashes preceding characters without a special meaning are left unmodified. A double quote may be quoted within double quotes by preceding it with a backslash.
In your example you forgot to mark $ with backslash as well. Difference between these two is explained perfectly by Adam here: Differences between single and double quota

Is this some kind of regex within bash variable instantiation?

What do those two assignations (i and C omitting the first one to void) do? Is it some kind of regex for the variable? I tried with bash, but so far there were no changes in the output of my strings after instantiating them with "${i//\\/\\\\}" or "\"${i//\"/\\\"}\""
C=''
for i in "$#"; do
i="${i//\\/\\\\}"
C="$C \"${i//\"/\\\"}\""
done
${i//\\/\\\\} is a slightly complicated-looking parameter expansion:
It expands the variable $i in the following way:
${i//find/replace} means replace all instances of "find" with "replace". In this case, the thing to find is \, which itself needs escaping with another \.
The replacement is two \, which each need escaping.
For example:
$ i='a\b\c'
$ echo "${i//\\/\\\\}"
a\\b\\c
The next line performs another parameter expansion:
find " (which needs to be escaped, since it is inside a double-quoted string)
replace with \" (both the double quote and the backslash need to be escaped).
It looks like the intention of the loop is to build a string C, attempting to safely quote/escape the arguments passed to the script. This type of approach is generally error-prone, and it would probably be better to work with the input array directly. For example, the arguments passed to the script can be safely passed to another command like:
cmd "$#" # does "the right thing" (quotes each argument correctly)
if you really need to escape the backslashes, you can do that too:
cmd "${#//\\/\\\\}" # replaces all \ with \\ in each argument
It's bash parameter expansions
it replace all backslashes by double backslashes :"${i//\\/\\\\}
it replace all \" by \\" : ${i//\"/\\\"}
Check http://wiki.bash-hackers.org/syntax/pe

bash: assign punctuation to variable

when I assign like this:
rmall="\,\.\?\!\:\;\(\)\[\]\{\}\"\'"
then echo $rmall, I got this:
\,\.\?\!\:\;\(\)\[\]\{\}\"\'
But what I want is only , How can I do?
,.?!:;()[]{}"'
as later I need to remove those.
Thank you
You are double quoting by using quotes and backslashes. Use one or the other.
Note: You will always need to use backslash for escaping your quote character but otherwise not needed.
Inside double quotes, only three escape sequences are treated specially:
\" is replaced by a literal "
\$ is replaced by a literal $
\\ is replaced by a literal \
These three are required to allow the literal character in contexts where they would normally produce special behavior. \", obviously, lets you include a double-quote inside a double-quoted string. \$ lets you output a literal dollar sign where it would otherwise trigger parameter substitution:
bash $ foo=5; echo "\$foo = $foo"
$foo = 5
\\ lets you output a literal backslash that precedes a parameter substitution or at the end of a string.
bash $ foo=5; echo "\\$foo"
\5
bash $ echo "Use a \\"
Use a \
A backslash followed by any other character is treated literally:
bash $ echo "\x"
\x

Must special characters in bash double quoted or escaped with \

Should I double quote or escape with \ special characters like ',
$ echo "'"
'
$ echo \'
'
Here is apparently doesn't matter, but are there situations where there is a difference, except for $, `` or`, when I know there is a difference.
Thanks,
Eric J.
You can use either backslashes, single quotes, or (on occasion) double quotes.
Single quotes suppress the replacement of environment variables, and all special character expansions. However, a single quote character cannot be inside single quotes -- even when preceded by a backslash. You can include double quotes:
$ echo -e 'The variable is called "$FOO".'
The variable is called "$FOO".
Double quotes hide the glob expansion characters from the shell (* and ?), but it will interpolate shell variables. If you use echo -e or set shopt -s xpg_echo, the double quotes will allow the interpolation of backslash-escaped character sequences such as \a, and \t. To escape those, you have to backslash-escape the backslash:
$ echo -e "The \\t character sequence represents a tab character."
The \t character sequence represents a tab character."
The backslash character will prevent the expansion of special characters including double quotes and the $ sign:
$ echo -e "The variable is called \"\$FOO\"."
The variable is called "$FOO".
So, which one to choose? Which everyone looks the best. For example, in the preceding echo command, I would have been better off using single quotes and that way I wouldn't have the confusing array of backslashes one right after another.
On the other hand:
$ echo -e "The value of \$FOO is '$FOO'."
The value of FOO is 'bar'.
is probably better than trying something like this:
$ echo -e 'The value of $FOO is '"'$FOO'."
Readability should be the key.

Resources