Bash script string comparison error, can't figure out - bash

I fixed the code below so it works:
#!/bin/bash
out="$(cat /proc/acpi/bbswitch)"
if [[ "$out" == *OFF* ]];
then
tee /proc/acpi/bbswitch <<<ON
echo "Nvidia card activated."
else
tee /proc/acpi/bbswitch <<<OFF
echo "Nvidia card disabled."
fi
This is made for activating or disabling my optimus card. I get an error on line 4:
./.bb: line 4: [0000:01:00.0 OFF: command not found
OFF
Nvidia card disabled.
I can read from it that it tries to execute the $out variable. Why?

You need to ensure that there is at least 1 space between the brackets [ / ] and the actual variables; i.e.: change your code from
if ["$out" == "$is"];
to:
if [ "$out" == "$is" ];
And it should work.
The reason is that [ is actually the "test" command in bash. Try on your prompt:
which [
and you should see something like:
/usr/bin/[
Also, man [ to read more about syntax
(Note, since arguments are delimited by spaces, there needs to be a space between your 2nd variable and ] as well. Test uses ] as the terminating sentinel)

Related

Not able to skip blank lines in a shell script

I am reading a text file line by line and taking the count of all lines as a part of my requirement.
When there is blank line then it get messed up. I tried with if condition for [ -z "$line" ] , however not able to succeed.
Here is my current code:
countNumberOfCases() {
echo "2. Counting number of test cases -----------"
cd $SCRIPT_EXECUTION_DIR
FILE_NAME=Features
while read line || [[ -n "$line" ]]
do
TEST_CASE="$line"
if [ "${TEST_CASE:0:1}" != "#" ] ; then
cd $MVN_EXECUTION_DIR
runTestCase
fi
done < $FILE_NAME
echo " v_ToalNoOfCases : = " $v_ToalNoOfCases
}
And below is Features file
web/sprintTwo/TC_002_MultipleLoginScenario.feature
#web/sprintOne/TC_001_SendMoneyTransaction_Spec.feature
web/sprintTwo/TC_003_MultipleLoginScenario.feature
#web/sprintOne/TC_004_SendMoneyTransaction_Spec.feature
When there is blank line it wont work properly so my requirement is that if there is blank line then it should be skipped and should not get considered.
You can write your loop in a little more robust way:
#!/bin/bash
while read -r line || [[ $line ]]; do # read lines one by one
cd "$mvn_execution_dir" # make sure this is an absolute path
# or move it outside the loop unless "runTestCase" function changes the current directory
runTestCase "$line" # need to pass the argument?
done < <(sed -E '/^[[:blank:]]*$/d; /^[[:blank:]]+#/d' "$file_name") # strip blanks and comments
A few things:
get your script checked at shellcheck for common mistakes
see this post for proper variable naming convention:
Correct Bash and shell script variable capitalization
see this discussion about [ vs [[ in Bash
Test for non-zero length string in Bash: [ -n “$var” ] or [ “$var” ]
about reading lines from a text file
Looping through the content of a file in Bash

Shell Script Syntax Error

At the moment I am working on a blackjack game using shell script. I have most of the script working with functions however the method I am using to find out if the player/computer goes bust doesn't seem to work. Could anyone point me in the right direction. (I am new to shell script.) When running it it will throw syntax errors around the lines that begin elif and sometimes if. It also prints all of the 'echo' outputs in bustConfirm instead of only the one that is true.
Also yes, one of my functions is called bustCheck.
bustConfirm(){
bust='bust'
under='under'
if [ $userBust -eq $bust -a $systemBust -eq $bust ]
then
echo "You both went bust! Be more careful!"
endGameRepeat
elif [ $userBust -eq $bust -a $systemBust -eq $under ]
echo $userName "went bust! Congratulations" $systemName"!"
endGameRepeat
elif [ $userBust -eq $under -a $systemBust -eq $bust ]
then
echo $systemName "went bust! Congratulations" $userName"!"
endGameRepeat
else
echo "Nobody went bust! Well played!"
endGameScores
fi
}
bustCheck(){
if [ "$userScore" -gt 21 ]
then
echo $userName "is bust!"
userBust='bust'
else
userBust='under'
fi
if [ "$systemScore" -gt 21 ]
then
echo $systemName "is bust!"
systemBust='bust'
else
systemBust='under'
fi
bustConfirm
}
The idea is that I wanted to use an && in the bustConfirm function and then an || to get the player is bust or system is bust result if only one of them was bust.
Also just a pointer but in the bustCheck I am seeing userBust and systemBust to contain the words bust or under. I created the variables bust and under for the bustConfirm function.
systemScore, userScore, systemName and userName are set before when the script is running.
Hope I've given enough detail and formatted it properly, first proper post so I apologize if not!
Taking a quick look, I see that the first if statement doesn't have a space after the opening square bracket.
I also recommend you put quotes around your variable names in if statements. This is due to the way shell actually works. The bash shell is extremely intelligent, and before your program has a chance to do anything, it grabs the line, does its magic, and then presents the line to the processor.
For example:
foo=""
if [ $foo = "" ]
then
echo "Foo is blank"
fi
Seems simple enough. However, what happens is that your shell will grab the line, substitute the value of $foo for the string "$foo", and then execute the line. Since $foo is blank, your if statement will become:
if [ = "" ] # That's not right!
then
echo "Foo is blank"
fi
By using quotes, this:
foo=""
if [ "$foo" = "" ]
then
echo "Foo is blank"
fi
becomes:
foo=""
if [ "" = "" ]
then
echo "Foo is blank"
fi
And that is valid. Another thing you can do is use the new test format that uses double square brackets:
foo=""
if [[ $foo = "" ]]
then
echo "Foo is blank"
fi
This will always work even without the extra quotes, and is now recommended unless you have to have your program compatible with the original Bourne shell syntax.
One more thing you can do in debugging your shell script is to use set -xv which turns on verbose debugging. Each statement, before it is executed will be printed, then it will print again after the shell fills in variables, patterns, etc., and then execute. It's a great way to debug your program. Just put set -xv on the line before you want this verbose debugging mode and use set +xv to turn it off. (Yes, the - turns it on and + turns it off.)
Thanks alot David, great answer, could you also tell me what the best way to get the && or equivalent of it within this as I need to find out if they are both bust, or just one etc
As already mentioned in a comment, you can use either one of these two forms:
if [ "$foo" = "bar" ] && [ "$bar" = "foo" ]
or
if [[ $foo = "bar" && $bar = "foo" ]]

multiple if condition in unix

I am trying to run the below logic
if [-f $file] && [$variable -lt 1] ; then
some logic
else
print "This is wrong"
fi
It fails with the following error
MyScipt.ksh[10]: [-f: not found
Where 10th line is the if condition , I have put in .
I have also tried
if [-f $file && $variable -lt 1] ; then
which gives the same error.
I know this is a syntax mistake somehwere , but I am not sure , what is the correct syntax when I am using multiple conditions with && in a if block
[ is not an operator, it's the name of a program (or a builtin, sometimes). Use type [ to check. Regardless, you need to put a space after it so that the command line parser knows what to do:
if [ -f $file ]
The && operator might not do what you want in this case, either. You should probably read the bash(1) documentation. In this specific case, it seems like what you want is:
if [ -f $file -a $variable -lt 1 ]
Or in more modern bash syntax:
if [[ -f $file && $variable -lt 1 ]]
The [ syntax is secretly a program!
$ type [
[ is a shell builtin
$ ls -l $(which [)
-rwxr-xr-x 1 root root 35264 Nov 19 16:25 /usr/bin/[
Because of the way the shell parses (technically "lexes") your command line, it sees this:
if - keyword
[-f - the program [-f
$file] - A string argument to the [-f program, made by the value of $file and ]. If $file was "asdf", then this would be asdf]
And so forth, down your command. What you need to do is include spaces, which the shell uses to separate the different parts (tokens) of your command:
if [ -f "$file" ]; then
Now [ stands on its own, and can be recognized as a command/program. Also, ] stands on its own as an argument to [, otherwise [ will complain. A couple more notes about this:
You don't need to put a space before or after ;, because that is a special separator that the shell recognizes.
You should always "double quote" $variables because they get expanded before the shell does the lexing. This means that if an unquoted variable contains a space, the shell will see the value as separate tokens, instead of one string.
Using && in an if-test like that isn't the usual way to do it. [ (also known as test) understands -a to mean "and," so this does what you intended:
if [ -f "$file" -a "$variable" -lt 1 ]; then
Use -a in an if block to represent AND.
Note the space preceding the -f option.
if [ -f $file -a $variable -lt 1] ; then
some logic
else
print "This is wrong"
fi

Bash script command result inside other variable to define prompt

I would like to define a prompt which will indicate with colors whether the command executed properly and whether the command was found. As for now I have something like this but I does not work properly.
PS1="\`COMMAND_RESULT=\$\?;
if [ $COMMAND_RESULT -eq 127 ]; then echo \[\e[33m\] ---=== Command not found ===--- ;
elif [ $COMMAND_RESULT -ne 0 ]; then echo \[\e[33m\]---=== \[\e[31m\]Oh noes, bad command \[\e[33m\]==---;
fi\`
\n\[\e[0;37m\][\[\e[1;31m\]\#\[\e[0;37m\]]
\[\e[0;32m\]\u\[\033[1;33m\]#\[\033[0;32m\]\h
As for now I get this error on bash start :
-bash: [: -eq: unary operator expected
-bash: [: -ne: unary operator expected
Don't pollute your PS1 with functions. You should use the special PROMPT_COMMAND variable to do this. The value of PROMPT_COMMAND is executed as a command prior to issuing each primary prompt.
Here is an example:
_check_command(){
local COMMAND_RESULT=$?
if [ $COMMAND_RESULT -eq 127 ]
then
echo -e "\e[1;33m---=== Command not found ===---\e[m"
elif [ $COMMAND_RESULT -ne 0 ]
then
echo -e "\e[1;31m---=== Oh noes, bad command ===---\e[m"
fi
}
PROMPT_COMMAND='_check_command'
PS1="\[\e[0;37m\][\[\e[1;31m\]\#\[\e[0;37m\]] \[\e[0;32m\]\u\[\033[1;33m\]#\[\033[0;32m\]\h "
There are many bash prompts you can find online to guide you. Here is one good example.
You probably should not escape $? as \$\?. Looks like it gets interpreted literally.
Also you can check out the Arch Wiki article that shows how to implement something similar to what you want. Look at this line:
PS1="$(if [[ ${EUID} == 0 ]]; then echo '\[\033[01;31m\]\h'; else echo '\[\033[01;32m\]\u#\h'; fi)\[\033[01;34m\] \w \$([[ \$? != 0 ]] && echo \"\[\033[01;31m\]:(\[\033[01;34m\] \")\$\[\033[00m\] "
especially this part:
([[ \$? != 0 ]] && echo \"\[\033[01;31m\]:(\[\033[01;34m\] \")

BASH: read in while loop

while [ $done = 0 ]
do
echo -n "Would you like to create one? [y/n]: "
read answer
if [ "$(answer)" == "y" ] || [ "$(answer)" == "Y" ]; then
mkdir ./fsm_$newVersion/trace
echo "Created trace folder in build $newVersion"
$done=1
elif [ "$(answer)" == "n" ] || [ "$(answer)" == "N" ]; then
$done=2
else
echo "Not a valid answer"
fi
done
Ok so I have this simple bashscript above that simply just tries to get input from a user and validate it. However I keep getting this error
./test.sh: line 1: answer: command not found
./test.sh: line 1: answer: command not found
./test.sh: line 1: answer: command not found
./test.sh: line 1: answer: command not found
Which I have no idea why because "answer" is nowhere near line 1. So I ran into this article
Which makes sense since it's referring to line 1 and can't find answer. So it seems to be starting a new subshell. However I didn't really understand the solution and can't see how I would apply it to my case. I just wanna get this to work.
$(answer) doesn't substitute the value of the variable answer. It executes answer as a command, and substitutes the output of that command. You want ${answer} everywhere you have $(answer). In this case you can get away with bare $answer too, but overuse of ${...} is good paranoia.
(Are you perhaps used to writing Makefiles? $(...) and ${...} are the same in Makefiles, but the shell is different.)
By the way, you have some other bugs:
In shell, you do not put a dollar sign on the variable name on the left hand side of an assignment. You need to change $done=1 to just done=1 and similarly for $done=2.
You are not being paranoid enough about your variable substitutions. Unless you know for a fact that it does the wrong thing in some specific case, you should always wrap all variable substitutions in double quotes. This affects both the mkdir command and the condition on the while loop.
You are not being paranoid enough about arguments to test (aka [). You need to prefix both sides of an equality test with x so that they cannot be misinterpreted as switches.
== is not portable shell, use = instead (there is no difference in bash, but many non-bash shells do not support == at all).
Put it all together and this is what your script should look like:
while [ "x${done}" = x0 ]; do
echo -n "Would you like to create one? [y/n]: "
read answer
if [ "x${answer}" = xy ] || [ "x${answer}" = xY ]; then
mkdir "./fsm_${newVersion}/trace"
echo "Created trace folder in build $newVersion"
done=1
elif [ "x${answer}" = xn ] || [ "x${answer}" = xN ]; then
done=2
else
echo "Not a valid answer"
fi
done
Which I have no idea why because
"answer" is nowhere near line 1. So I
ran into this article
That's not your problem here.
I ran the script and did not get the error you got. I did receive the error:
./test.sh: line 1: [: -eq: unary operator expected
when I tried to compile though. Defining done fixed this. The following script should work...
#!/bin/bash
done=0
while [ $done -eq 0 ]
do
echo -n "Would you like to create one? [y/n]: "
read answer
if [[ "$(answer)" == "y" || "$(answer)" == "Y" ]]; then
mkdir ./fsm_${newVersion}/trace
echo "Created trace folder in build $newVersion"
$done=1
elif [[ "$(answer)" == "n" || "$(answer)" == "N" ]]; then
$done=2
else
echo "Not a valid answer"
fi
done
...note you were doing string comparisons on your done variable, which you apparently intended to be numeric. It's generally bad form to do string comparison on a numeric type variable, though it will work. Use -eq (arithmetic comparison operator) instead. (Also note that if you kept that test, your string equality would be inconsistent... you had "=" in one spot and "==" in another spot... nitpicking here, but it's helpful to be consistent).
Also, I suggest double brackets for your compound conditionals as they will be more readable if you have longer ones. e.g.
if [[($var1 -eq 0 && $var2 -eq 1) || ($var1 -eq 1 && $var2 -eq 0)]]; then
Just a matter of preference as you only have two conditions, but could be useful in the future.
Also you were missing braces '{' '}' around your newVersion variable.
Finally, I'd suggest putting the line #!/bin/bash on the top of your script. Otherwise its up to your environment to determine what to do with your script, which is a bad idea.

Resources