Single and double quotes in the same bash input - bash

I want to give input to a bash read function which contains both single and double quotes at the same time, then have it compare with a stored value.
This script asks a random question on startup, looking for correct syntax of some commands I want to memorize. However some of these commands contain both single and double quotes, so how to handle this with bash?
#!/bin/bash
echo "What is the secret string?"
read secret
if [ "$secret" = "123" ]
then
echo "You're Awesome!!!"
else
echo "You're memory sucks!!"
fi
So if $question is "Hi my name is 'Ed'" including the single AND double quotes in the same input, how do I work this magic?

There is no magic to be worked. The input from the user never becomes syntax within your script — it is never evaluated as code — so it can contain anything.*
* Except for the NUL character (\0) itself, since that is the string terminator character.

reading strings that contain quotes doesn't require anything special, but I'd use read -r to prevent it messing with backslashes. You can also use the -p option to supply a prompt (instead of echoing it separately):
read -r -p "What is the secret string? " secret
The only tricky thing here is specifying the string to compare with. One option is to express it as a double-quoted string, and escape the double-quote(s) within it with backslashes. You'll also need to escape any dollar signs, backticks, or backslashes the same way:
if [ "$secret" = "dq: \", sq: ', dollar: \$" ]
then
echo "You're Awesome!!!"
else
echo "Your memory sucks!!"
fi
Here's an example run of the above code:
$ ./secrettest.sh
What is the secret string? dq: ", sq: ', dollar: $
You're Awesome!!!

My question was about specifying the string in the code as Gordon mentioned, not reading it from the input. I had escaped the double quotes and single quotes in my original which is why it didnt work.
So this
if [ "$secret" = "command \'with \"options\"\'" ]
Should have been this
if [ "$secret" = "command 'with \"options\"'" ]
Thanks guys.

Related

I want to print $ in shell, but it is always deleted

In a Linux shell, I want to print:
$300
$400
But when I do echo -e "$300\n$400" it shows:
00
00
When I do printf "$300\n$400" it shows the same thing!
So why does shell delete my dollar sign and the number right after it? Is there a way to print what I want?
You need to escape dollar $, since you are using double quotes, This will ensure the word is not interpreted by the shell.
$ echo -e "\$300\n\$400"
$300
$400
You may be aware how to access variables,
Example :
$ test="foo"
$ echo "$test"
foo
Suppose if you want to print $test, then you have use either
$ echo "\$test"
$test
OR with single quotes
$ echo '$test'
$test
In the shell, the $ character has a special meaning. It means "replace the $ and the following word or digit or special character with the value of a variable of that name". For example:
currency='EUR'
echo "The currency is $currency"
The variables 0, 1, 2, etc. contain the command line arguments to the program. So if you run your program as my-program Hello, world, you can write this code:
echo "argument 1 is $1"
echo "argument 2 is $2"
echo "both together are $1 $2, and all arguments are $*"
To make the $ character lose this special meaning, it must be written as \$. For example:
price=123
echo "The price is $price\$"
The first $ refers to the variable, and the second $ is escaped.
Alternatively you can surround your string in 'single quotes', which removes the special meaning of all characters.
To learn more about this topic, run the man bash command and read the section about variable expansion.
$ has special meaning to the shell; when it sees a $, it expects an existing shell variable name to follow. For example, $PATH.
In your case, you don't want the shell to think that you're trying to print out the value of shell variables, so you must tell the shell that the $ is indeed what you want to be displayed. This is done by preceding it with a backslash as explained in other answers.
Adding a backslash before characters is called escaping them (yes, not the most obvious terminology), and you are already using some escape characters unknowingly. (\n)
This applies to display other operators too, such as =, :, etc. Hope that helps.
You can use single quote. Enclosing characters in single-quotes (') shall preserve the literal value of each character within the single-quotes, where as enclosing characters in double-quotes(") shall preserve the literal value of all characters within the double-quotes, with the exception of the characters back quote, dollar-sign, and backslash.
echo -e '$'300"\n"'$'400

How to have both semicolons and variables in a URL with cURL?

My question is how can I make this bash code run:
#!/bin/sh
actionString = printpage
curl 'www.example.com/index.php?action=$actionString;post=5'
My problem is that if I do not escape the URL with quotation marks, then it will stop processing the URL after the ";", however if I do have it in quotation marks it won't recognize the variable. Is there some trick to getting past this? Thanks for the help.
Use the quotes that don't inhibit parameter substitution.
curl "http://www.example.com/index.php?action=$actionString;post=5"
Variables are NOT evaluated within single quotes:
greeting=hello
echo 'say $greeting; ...' # outputs literally: say $greeting; ...
Variables ARE evaluated within double quotes:
echo "say $greeting" # outputs: say hello; ...
You can quote only the part of parameters that needs quoting, and you can use double quotes for some parts and single quotes for others. All of these are equivalent:
echo say $greeting"; ..."
echo say $greeting'; ...'
echo "say $greeting"'; ...'
echo say $greeting\; ...
So in your case, these would all work:
curl "http://www.example.com/index.php?action=$actionString;post=5"
curl http://www.example.com/index.php?action="$actionString;post=5"
curl http://www.example.com/index.php?action="$actionString;"post=5
and if actionString doesn't have special characters, this would work too:
curl http://www.example.com/index.php?action=$actionString';post=5'

How can I use a value which includes "" within an echo?

I wrote a bash script which automatically configures a setting file for some application.
The application uses a similar syntax to /etc/sysconfig/network file, the only exception is that it requires the values to be in double quotes " ".
So the line in the script looks something like this, but I don't know how to allow the double quotes of the ip address within the echo:
echo " ipaddr="1.1.1.1" " > file
How can it be done?
Escape the quotes with a backslash character or use single quotes:
echo "ipaddr=\"1.1.1.1\""
echo 'ipaddr="1.1.1.1"'
The other answers all provide excellent ways of solving your problem. I'd just like to add one using printf, that can make things comfortable if, e.g., the ip address is stored in a variable:
ip=1.1.1.1
printf 'ipaddr="%s"\n' "$ip" > file
But here again, as in the other answers, you'll need to play with both, single and double quotes.
Things will be comfortable in this case (still assuming the ip is in the variable ip), because with echo instead of printf, you'd have to use either:
echo "ipaddr=\"$ip\"" > file
# or
echo 'ip addr="'"$ip"'"' > file
(ok, it's not a big deal, but I usually like printf better than echo in bash).
You have at least two options:
Escape the double-quotes with single-quotes:
echo ' ipaddr="1.1.1.1" ' > file
Escape the double-quotes with backslashes:
echo "ipaddr=\"1.1.1.1\"" > file
In general, you can use a backslash to escape any single character; and you can use one type of quote to escape the other.
you got the choice:
you can use different quotes inner and outer of the echo
echo "ipaddr='1.1.1.1'" > file
echo 'ipaddr="1.1.1.1"' > file
you can escape the quotes:
echo "ipaddr=\"1.1.1.1\"" > file
or you can make it simpler (but with escapes on the quotes):
echo ipaddr=\"1.1.1.1\" > file

Which form is preferable to use in Bash?

I'm studyng Bash, and I see that the form
C=example
echo "$C"
give the same result of the form
C="example"
echo $C
I'd like to know if is better put the " " in the assignment of the variable or after the $. Or if it is indifferent. or if one is consider "more beautiful" than the other.
If you're certain that a variable's value is a single word (no white space) then it's OK to use $varname or ${varname}. If you can't guarantee this, then you should use "$varname" or "${varname}". Note that bash does word-splitting before interpreting your command, so you may actually get a syntax error if you don't quote the expression, for example
C="white space"
if [ -z $C ]
then
...
fi
will result in syntax error:
-bash: [: white: binary operator expected
while this works fine:
C="white space"
if [ -z "$C" ]
then
...
fi
This is due to the fact after variable expansion in the first, unquoted case bash sees this:
if [ -z white space ]
then
...
fi
and the -z operator expects just one, not two arguments. In the second, quoted case bash sees this:
if [ -z "white space" ]
then
...
fi
i.e. just a single argument as required. Note also that quotes were used in assignment
C="white space"
as it would also produce an error if you wrote
C=white space
since this would mean: execute command space with environment containing an added variable C=white.
So, in general you should quote these expressions to ensure your code is more robust against unforeseen variable values. This is especially true if the variable value comes from input, file etc. It is usually safe to drop the quotes for integer variables or when you just want to display the value of a variable as in echo $C.
It matters when the string contains whitespace characters. Without the quotes, whitespace characters are treated as token delimiters and bash tries to interpret the substituted string as an expression.
Always put quotes to be safe, when you don't intend to evaluate the variable as a part of the expression.
Imagine you change the input from "example" to "two words", then you could encounter strange behaviour or even syntax errors when executing the script, in case you have overlooked the above.
In other words,
C="abc def"
# the echo command receives one argument: "abc def"
echo "$C"
# echo receives two arguments: "abc" and "def"
echo $C
# bash tries to execute the program "abc" with a first argument "def"
$C
# bash tries to execute the program "abc def"
"$C"
A good documentation about quotes and word-spliting :
"USE MORE QUOTES!" They are vital. Also, learn the difference between ' and " and `. See http://mywiki.wooledge.org/Quotes and http://wiki.bash-hackers.org/syntax/words
greybot sample from IRC freenode #bash is talking to the world =)
If it's a one-word constant, it's irrelevant.
However, you should read about the two kinds of quoting. Try this article and this documentation. There is also a SO question.
Try with a real example with whitespace. For the string example you do not need any quoting at all. So create a file called This is an example.txt and then retry. Substitute echo with ls...

linux shell script: Ignore double quotes from IFS

Due to the default value of IFS, I am getting following result.
STR="this is a \"valid string\""
for a in $STR; do
echo $a;
done
will give output as:
this
is
a
"valid
string"
But I don't want to split "valid string".
I need output as:
this
is
a
"valid string"
So, how can I make shell ignore double quotes from IFS?
There are two types of quotes: Literal ones, and syntactic ones. The outer quotes of STR are syntactic, which means that they are not actually part of the string, they are just used to tell Bash where the string starts and ends. The inner (escaped) quotes are literal, so they are part of the string. In other words, the literal value of your string is:
this is a "valid string"
Now if you want to loop over this, you have to remember that this string consists of five (space-separated) literal words:
this
is
a
"valid
string"
If you want to get around this, one you can either use eval (but beware of the serious security issues) or arrays:
declare -a STR=(this is a "valid string")
for str in "${STR[#]}"
do
echo "$str"
done
That's not easily possible. You could switch to Perl instead. Or, if you have full control over the string, and nobody can possibly ever insert something evil into it, you could use this code:
str="a b c \"d e\""
eval "set args $str; shift"
for i in "$#"; do
echo "$i"
done
This will output the last word without the quotes, though. And it overwrites the command lune arguments to the shell program itself.
If you need to keep the quotes, this is called parsing and should not be done by a shell program, since it becomes too complicated.

Resources