Bash if else string variable comparison - linux

I am writing a shell script at which I am trying to compare 2 variables that are strings. Every thing works fine as in all the variables have a value from the commands however my if then else statement is not working.
#!/bin/bash
name=$1
for i in {1...10}
do
username=sudo cat /class/rolls/CSCE215-*|awk 'BEGIN {FIELDWIDTHS = "32 20"} {print $2}'|cut -d " " -f6 |sed -n '1,1p'
if ["$name" == "$username"]
then
echo Correct Username
else
echo Incorrect Username
fi
done
All of the tutorials and help online appears to have what I have here but I cannot find out what is wrong with mine.

You are using the "classic test" but it's a good idea to ALWAYS use the newer conditional expression: http://wiki.bash-hackers.org/syntax/ccmd/conditional_expression
if [[ "$name" == "$username" ]]
As far as I know the test command (with a single bracket) doesn't even officially support the "==" operator..
Oh, and of course don't forget the spaces inside the brackets, bash needs them to break the line up into words.

When using test or [, the correct comparison is:
test "$string1" = "string2"
or
[ "$sting1" = "$string2" ]
Note: the single = instead of ==, and always quote sting variables. Further, there is nothing wrong with using the test or [ operators, in fact, they are preferred when portability is needed. They simply lack some of the extended functionality of the [[ operator, such as character class comparison and the ability to use =~.
Now when using the [[ operator, the correct form is:
[[ "$sting1" == "$string2" ]]
Note: as pointed out, quotes are not required when using the [[ operator, but if you get in the habit of always quoting strings, you will be safe in both cases.

Related

What would cause the BASH error "[: too many arguments" after taking measures for special characters in strings?

I'm writing a simple script to check some repositories updates and, if needed, I'm making new packages from these updates to install new versions of those programs it refers to (in Arch Linux). So I made some testing before executing the real script.
The problem is that I'm getting the error [: excessive number of arguments (but I think the proper translation would be [: too many arguments) from this piece of code:
# Won't work despite the double quoted $r
if [ "$r" == *"irt"* ]; then
echo "TEST"
fi
The code is fixed by adding double square brackets which I did thanks to this SO answer made by #user568458:
# Makes the code works
if [[ "$r" == *"irt"* ]]; then
echo "TEST"
fi
Note that $r is defined by:
# Double quotes should fix it, right? Those special characters/multi-lines
r="$(ls)"
Also note that everything is inside a loop and the loop progress with success. The problems occurs every time the if comparison matches, not printing the "TEST" issued, jumping straight to the next iteration of the loop (no problem: no code exists after this if).
My question is: why would the error happens every time the string matches? By my understanding, the double quotes would suffice to fix it. Also, If I count on double square brackets to fix it, some shells won't recognize it (refers to the answer mentioned above). What's the alternative?
Shell scripting seems a whole new programming paradigm.. I never quite grasp the details and fail to secure a great source for that.
The single bracket is a shell builtin, as opposed to the double bracket which is a shell keyword. The difference is that a builtin behaves like a command: word splitting, file pattern matching, etc. occur when the shell parses the command. If you have files that match the pattern *irt*, say file1irt.txt and file2irt.txt, then when the shell parses the command
[ "$r" = *irt* ]
it expands $r, matches all files matching the pattern *irt*, and eventually sees the command:
[ expansion_of_r = file1irt.txt file2irt.txt ]
which yields an error. No quotes can fix that. In fact, the single bracket form can't handle pattern matching at all.
On the other hand, the double brackets are not handled like commands; Bash will not perform any word splitting nor file pattern matching, so it really sees
[[ "expansion_of_r" = *irt* ]]
In this case, the right hand side is a pattern, so Bash tests whether the left hand side matches that pattern.
For a portable alternative, you can use:
case "$r" in
(*irt*) echo "TEST" ;;
esac
But now you have a horrible anti-pattern here. You're doing:
r=$(ls)
if [[ "$r" = *irt* ]]; then
echo "TEST"
fi
What I understand is that you want to know whether there are files matching the pattern *irt* in the current directory. A portable possibility is:
for f in *irt*; do
if [ -e "$f" ]; then
echo "TEST"
break
fi
done
Since you're checking for files with a certain file name, I'd suggest to use find explicitly. Something like
r="$(find . -name '*irt*' 2> /dev/null)"
if [ ! -z "$r" ]; then
echo "found: $r"
fi

When should I use "" to quote a value in shell test and in echo?

I'm writing bash script like this:
VF_ETH=$(command)
if [ -n "$VF_ETH" ] ; then
echo "ixgbevf eth: "$VF_ETH
fi
command is a linux command, my question is:
$VF_ETH is to get value of VF_ETH, why use "" to quote it in line2 in shell test?
if I do not use "" to quote it, will test failed?
if use "" to quote a value is to make it into string, why not use in echo in line3?
Thank you
Assuming you get an actual command stored in VF_ETH variable, which contains spaces. Now if you use if [ -n $VF_ETH ] and when shell expands the variable, there will be multiple parameters to -n whereas it expects only one. Hence you might get something like binary operator expected error.
Also in the echo command, it is not mandatory to have only one parameter. Hence even if you are not using double quotes, it works.
Hence to avoid it, always use double quotes while expanding variables.
Also use https://www.shellcheck.net/ to check your script and it will give you correct information on where your script is wrong/not as per standard.
You should always double quote variables used in command line arguments and within [ ... ] tests, for example:
ls "$var"
echo "$var"
[ -f "$var" ]
test -f "$var"
In all the above examples the commands used will receive different values with and without the double quotes, when the value of $var starts with a bunch of spaces and contains some spaces. For example:
var=" an apple"
echo "$var" # prints " an apple"
echo $var # prints "an apple", without the leading space
You don't need to quote them in simple assignments, for example:
a=$var
a=$var$b
If the assignment contains spaces or other special characters, then you have to quote, or escape those special characters:
a="$var $b"
a=$var\ $b
a=$var" "$b
All the above are equivalent, but the first one is probably the easiest to read, and therefore recommended.
You don't need to quote special variables that never have unsafe values, for example:
test $? = 0
If you're unsure, or not yet confident, then a good role of thumb is to double quote always.
For 1. and 2. If you set $VF_ETH="x -a -z x" and test it with code:
if [ -n $VF_ETH ] ; then
echo yes
else
echo nope
fi
the output will be nope as the the inside of the square brackets would expand to -n x AND -z x (nonempty and empty). Adding quotes around the variable would fix that.
Set $VF_ETH="*". Now if you echo $foo bash would do wildcard expansion and echo would output the contents of your current directory.

bash evaluate if statement from string

I have a file which contains shell test statements.
I need to evaluate the expression.
For example:
a="something"
fromfile='[ "$a" == "something" ]'
if `$fromfile`; then
echo "true"
else
echo "false"
fi
It always throws false.
I tried with `` and eval, but still not works.
What is the solution?
I guess you just didn't hit the right syntax. You really need eval for this:
a="something"
fromfile='[ "$a" = "something" ]'
if eval "$fromfile"
then
echo "true"
else
echo "false"
fi
But please read about the dangers eval imposes on the security side! If you plan to execute strings you are given, if only in part, (by the user, from a data base, from a web site, ...) you might introduce a security risk. (Consider fromfile='rm -rf ~' ← In case you don't understand this: don't try this! It will remove everything from your home directory!)
There are better options in most cases like declaring shell functions and passing their names instead of a complete syntax-containing string.

Check if line in file contains a pattern in Bash

I'm trying to figure out why this wont check the lines in the file and echo
How do you compare or check if strings contain something?
#!/bin/bash
while read line
do
#if the line ends
if [[ "$line" == '*Bye$' ]]
then
:
#if the line ends
elif [[ "$line" == '*Fly$' ]]
then
echo "\*\*\*"$line"\*\*\*"
fi
done < file.txt
The problem is that *Bye$ is not a shell pattern (shell patterns don't use the $ notation, they just use the lack of a trailing *) — and even if it were, putting it in single-quotes would disable it. Instead, just write:
if [[ "$line" == *Bye ]]
(and similarly for Fly).
If you want to use proper regular expressions, that's done with the =~ operator, such as:
if [[ "$line" =~ Bye$ ]]
The limited regular expressions you get from shell patterns with == don't include things like the end-line marker $.
Note that you can do something this simple with shell patterns (*Bye) but, if you want the full power of regular expressions (or even just a consistent notation), =~ is the way to go.

Reason of adding "_" in the if comparison "if [ "_$str" = "_" ]; then ....; fi"

I see many times in the shell scripts that they use "_" in the if comparison as indicated below:
if [ "_$str" = "_" ]; then ....; fi
the above code check if the str variable is empty with the comparison if [ "_$str" = "_" ].
Why do not use if [ "$str" = "" ]? why adding "_" in both strings?
As far as I know it is for historical reasons. There once were (and maybe on some obscure systems still are) shells which had (have) trouble handling empty strings (and maybe strings starting with dashes). Hence the simple idea to prevent empty strings (and option-like looking ones) altogether by adding a prefix. All common shells today do not have these problems anymore.
I can only guess that before learning how to use quotes properly, the writers of the scripts you've seen had trouble with something like
if [[ $str = "" ]]; then ....
which will fail if $str is empty, as the argument expands to nothing. Whereas
if [[ _$str = _ ]]
will work, but is a kludge.
Remember that [ is an alias to the command test. The question is what would happen if your string started with a dash?
In old versions of test when using the standard Bourne shell, you'd get an error:
$ test -gt = "some_string"
invalid argument
The same thing would happen with an if
$ string="-gt"
$ if [ "$string" = "some_other_string" ]
> then
> echo "Match"
> else
> echo "No Match"
> fi
if: invalid argument
That's because the test command would see the parameter with a dash and assume that it's a command argument, then either it's an invalid command argument, or the format of the command is incorrect. If you use [[ instead of [, it's not an issue at all because [[ is a built in test for the if.
This is no longer an issue. The [ and test are internal commands to both the Kornshell and BASH, and these shells can handle this issue. Even the newer versions of test are no longer thrown:
$ test -gt = "some_string" # No error.
$ echo $?
1
However, older scripts, and those people who either learned from an older timer, or were back writing scripts when system with 32 Mb of memory running on a 16 Mhz 386 chip with Xenix was a state of the art system have gotten into the habit. I use to use x:
if [ x$var = x$foo ]

Resources