shell script variable use - bash

I'll get to the meat and bones:
MY_VAR=6
until [$MY_VAR = 0]
do
dir/dir_$MY_VAR.log
ps | grep "NAME_$MY_VAR.ksh"
check some things
if [results = ok]
echo "program $MY_VAR sucessful"
else
echo "program $MY_VAR failure"
MY_VAR = `expr $MY_VAR - 1`
done
Now I am getting the following errors MY_VAR not found and [6: not found, so I'm assuming a rather noobish mistake. I feel the logic is sound enough just a simple syntax error I am making somewhere by the looks of the two errors I think it could be in the declaration.

You need to have a space after [ and before ] since [ is actually a command and not a delimiter.
Here is your script re-written in Bash (or ksh):
my_var=6
until ((my_var == 0))
do
dir/dir_$my_var.log # I have no idea what this is supposed to be
ps | grep "NAME_$my_var.ksh"
# check some things
if [[ $results = ok ]]
then
echo "program $my_var successful"
else
echo "program $my_var failure"
((my_var--))
fi
done
However:
for my_var in {6..1}
do
dir/dir_$my_var.log # I have no idea what this is supposed to be
ps | grep "NAME_$my_var.ksh"
# check some things
if [[ $results = ok ]]
then
echo "program $my_var successful"
else
echo "program $my_var failure"
fi
done

Your two errors are caused by:
until [$MY_VAR = 0]
MY_VAR = $(expr $MY_VAR - 1)
[I've used $() instead of backticks because I couldn't get backticks into the code section]
The first problem is the lack of spaces around the square brackets - on both ends. The shell is looking for the command [6 (after expanding $MY_VAR), instead of [ (have a look at /usr/bin/[ - it's actually a program). You should also use -eq to do numeric comparisons. = should work ok here, but leading zeros can break a string comparison where a numeric comparison would work:
until [ "$MY_VAR" -eq 0 ]
The second problem is you have spaces in your variable assignment. When you write MY_VAR = ... the shell is looking for the command MY_VAR. Instead write it as:
MY_VAR=`expr $MY_VAR - 1`
These answers directly answer your questions, but you should study Dennis Williamson's answer for better ways to do these things.

Related

How to check if a variable is set in a string in bash?

Let assume I have something like below:
eval link='/var/lib/${XYZ}/test' # string from another text file
XYZ is just for the the example and it could be anything like below:
eval link='/var/lib/${MY_OWN_VAR}/test' # single quote here not double quotes
eval link='/var/lib/${WHAT_EVER}/test'
Is it possible to error out if XYZ is not set? or is there any other way to figure out if XYZ is set or not?
I have looked at this, but the assumption there is that you know the variable name. In my case I have no control over what would be in the string to be evaluated.
UPDATE
To be clear, all the strings that needs to be evaluated are from a text file. basically a program reads a text file and
outputs the evaluated strings.
All I am trying here is to figure out a way to gracefully catch "unbound variable" error while evaluating any string. basically what set -u does but gracefully.
You can test the eval in a subshell before performing it for real:
assign_if_defined(){
echo 1>&2 "testing $1=$2"
outvar="$1"
input=${2#Q}
err=$(exec 2>&1; set -u; eval "${outvar}=${input#P}")
if [ -z "$err" ]; then
echo 1>&2 "eval test succeeded: doing for real"
eval "${outvar}=${input#P}"
else
echo 1>&2 "eval test failed: not doing for real"
echo 1>&2 "error: $err"
fi
}
A=set
assign_if_defined link1 "'"'"\/${A}/'
echo link1=$link1
unset B
assign_if_defined link2 '/$B/'
echo link2=$link2
It seems that the #Q/#P transformations first appeared in bash 4.4. Using them means that quoting is much simplified. If using an older version of bash, you could try normal quoting (eval "${outvar}=\"${input}\"") but the code will fail if input contains special characters (as the first example).
Well I don't know exactly how much control (or knowledge) you do have on the strings, but can't you just test if it's empty?
VAR=mydirectory
str=/var/lib/${VAR}/test # valid
str2=/var/lib/${NONEXISTANT}/test # invalid
if [[ "$str" = "/var/lib//test" ]] ;
then
echo 'is_empty';
else
echo 'is_set';
fi;
The only downfall is that the test will fail if you receive a var that is set but empty, e.g. VAR=""

if statement always goes to the else

I just started learning Bash scripting and i have to do a program that separate between one bit map image to two (the image is broken), I already found on the web how to write loops and statements
but i don't know why my if statement is always goes to the else.
the if is modulo by 2 thats equals to 0
here is the following code
#!/bin/sh
OUTPUT="$(hexdump -v -e '/1 "%02X\n"' merge.bmp)"
echo $OUTPUT
vars=0
count=1
touch one
touch two
for i in $OUTPUT
do
if (($vars%2==0))
then
echo "1"
else
echo "2"
fi
vars=$((vars+count))
done
in the terminal the following error is
./q3.sh: 14: ./q3.sh: 2885%2==0: not found
2
i really don't know why the if always print 2
The shebang line is wrong, it should be:
#!/bin/bash
((expression)) is a bash extension, not available in sh.
The /bin/sh version of the (()) bashism is this:
if test $(($vars % 2)) -eq 0; then
echo "1"
...
fi
Since $(()) knows about variable names, you may even drop the dollar and write
if test $((vars % 2)) -eq 0; then
echo "1"
...
fi

While loop in Bash not running

I'm pretty new with Bash scripting and am having trouble getting my 'while' loop to run. When I echo keywords, a whole list of words prints and then when I echo length, it prints 124. I believe I'm using the while loop and condition correctly, so I can't figure out what I'm doing wrong. Any thoughts?
keywords=$1
length=${#keywords}
echo "$keywords"
echo "$length"
if [ -z "$keywords" ]; then
while [ $length -gt 100 ]; do
echo "$keywords"
echo "$length"
keywords="${keywords%,*}"
length=${#keywords}
done
fi
echo $keywords
The problem is here:
[ -z "$keywords" ]
-z is true if its argument is an empty string. Something of length 124 is definitely far from empty. You probably meant -n.
Next time, please also include the input in the question so we can reproduce the problem.

Shell script: Why can't the if statement make a logical comparison?

I'm new to Unix and Linux in general and failed to make a logical comparison within an if statement.
I'm sure this is a very basic mistake but I just can't find the error.
if (7+3 == 10); then
echo "nice this works"
elif (7+3 == 73); then
echo "too bad string concatenation"
else
echo "I really don't understand shell"
fi
Echo: I really don't understand shell.
I would expect you to see this error message twice: 7+3: command not found -- did you?
Single sets of parentheses run the enclosed commands in a subshell, so you're attempting to execute the command 7+3 with two arguments, == and 10 (or 73)
Arithmetic evaluation occurs within double parentheses
if ((7+3 == 10)); then
echo "nice this works"
elif ((7+3 == 73)); then
echo "to bad string concatenation"
else
echo "I really don't understand shell"
fi
nice this works
See http://mywiki.wooledge.org/ArithmeticExpression
The operator your are using == is used for string comparisons. The right way to do it would be with the -eq (equal) operator:
if [ 10 -eq 10 ]; then
echo "10 = 10"
fi
and you also need to use the right parenthesis for the if [ ] (you can look this up in the bash with 'man test')
The correct syntax would be
if [ $((7+3)) -eq 10 ]; then
echo "nice this works"

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" ]]

Resources