when to use double quotes in if-condition in shell scripts - shell

I'm learning shell scripting for an exam and in our teachers book at one place he writes in an example the following:
# This script expects a folder path as an argument.
cd $1
if [ $? -ne 0 ]; then echo "Folder not found"; exit 1; fi
In another example however he writes:
# This script expects one argument
if [ "$#" -ne 1 ]; then echo "Missing Parameter"; exit 1; fi
Now, when do I have to put the tested argument within the square brackets in double quotes and when not?
I mean, $? in this case represents a number. However, $# in this example also represents a number and not a string (although everything is a string). But why is $# double-quoted and $? not?

There is no difference, since Bash guarantees that both are numbers.
If there was a possibility that a variable might be a string, potentially with control characters or spaces, then it is necessary to quote.

They mean the same thing. $values represent variables and will be used as variables inside and outside the quotes.

Related

How to extract the length of the input string of bash shell program?

Here are the code in
#! /bin/bash
file=$1
echo ${#$1}
echo ${#file}
length=${#file}
my input in the shell are test.sh
line 6: ${#$1}: bad substitution
So how can I print length of my input and assigned as a variable in bash shell program for subsequently use?
The problem you are seeing is, that in the line $1=file, the $1 gets expanded to your first argument and this is run as a command.
So if you run your script with the argument good, bash will run the command good=file which is just wrong.
The solution is obviously to get the assignment right:
file=$1
Also, your ${#$1} gets a bit much of special characters. Like ${#file} the string after the # is the variable name, which in the case of arguments is just a number, so it should be ${#1} instead.
In general, I find complex parameter substitution with arguments quite horrible to read (and in the case of arithmetic expressions they are of coruse quite impossible), so I tend to always assign properly named variables to them (so I use x=$1; echo ${x} rather than echo $1)
After that, your script does what it is supposed to do:
#! /bin/bash
file=$1
echo ${#1}
echo ${#file}
length=${#file}
echo "length: ${length}"

Shell script nested loop (while within for loop) does not work

My shell script, It executes its inner loop correctly, but only execute its outer loop once.
Please kindly check my script, why it does not work.
OS: Amazon EC2 Linux
I created shell script, to delete files, from a directory, with filenames,
that are not exist in my created TXT file.
for entry in "/home/ec2-user/upload/upload/*"
do
exist=false
file="/home/ec2-user/upload/requiredjpg.txt"
while IFS= read -r line
do
if [ "$line" = "$entry" ]
then
echo "same"
else
echo "not same"
fi
done <"$file"
echo $exist
if [ $exist = false ]
then
echo $exist
fi
done
Expression "/home/ec2-user/upload/upload/*" is interpreted, literally, as string /home/ec2-user/upload/upload/*. Obviously, you expected it to be expanded to an array of paths matching the wildcard expression. But, in double quotes, the asterisk character has different meaning, -- it is only used in shell parameter expansions (which is not present in the expression in question.)
What you really need is filename expansion. In order to activate it, you simply need to put the asterisk character outside the double quotes:
"/home/ec2-user/upload/upload/"*
Note that you don't need the double quotes in this particular string, since there is no any special character in it that needs to be escaped (quoted).
There is misplaced 'do', in the inner loop. The 'if' statements should come under it.
while IFS= read -r line
do
if [ "$line" = "$entry" ]

Hp-Ux.Shell.Using variables in "if"

Recently I've got confused by the following situation.
What is the difference between two if usage:
Case 1
amount=10
if [[ $amount -eq 10 ]]
then
echo "something"
fi
script output:
$ ./1.sh
something
Case 2
if [[ amount -eq 10 ]]
This also works like this (note that the variable name doesn't contain the $).
So the question is how does it work even without dollar sign in the variable name.
P.S. I'm using a POSIX shell on HP-UX.
man bash
ARITHMETIC EVALUATION
...
Shell variables are allowed as operands; parameter expansion is per‐
formed before the expression is evaluated. Within an expression,
shell variables may also be referenced by name without using the
parameter expansion syntax.
In this context shell does not expect anything but numerics, so it expands strings as variables. That makes sense to me.

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...

Explain this bit of code

Could somebody explain what this bit of code means please ?
I believe the second line is "if the exit status is zero", then echo "valid command" but I dont understand the first line
$# &>/dev/null
if [[ $? = 0 ]]
then
echo "Valid command"
fi
The first line runs the command formed by simply using all arguments to the script, and redirecting the output to /dev/null which essentially throws it away.
The built-in variable $# expands to all of the positional parameters, with each parameter is a quoted string, i.e. the parameters are passed on intact, without interpretation or expansion. To get this effect, I believe you need to quote the use of the variable, i.e. say "$#".
The operator &> redirects both stdout and stderr.
According to the manual, $# expands to the positional parameters, starting from one. If you call this script as scripty.sh ls /, it will execute ls / while redirecting all output to the bit bucket. That should return success (I hope!) and thus the script will print Valid command. If you call it scripty.sh ls /some/nonexistent/directory then the ls command should fail, and the script will output nothing.
Actually, I think the script can be improved to putting double quotes around $# so that arguments with spaces in them don't trip up the interpreter.
With $# the command ls "/Library/Application Support" is expanded to three words. With "$#" it's expanded to two, and the command is run just as it would be without the script wrapping it.
I'd like to add that this is unnecessarily verbose and could be shortened to
if "$#" &>/dev/null
then
echo "Valid command"
fi
or even shorter
"$#" &>/dev/null && echo "Valid command"

Resources