Shell script: use while loop to check string contents using [ ] - shell

I am trying to check a string that is output from a program, if the string matches a certain content, the while-loop will stop the program. At the same time, I need to count how many times the program has run:
x = "Lookup success" # this is supposed to be the output from the program
INTERVAL=0 # count the number of runs
while ["$x" != "Lookup failed"] # only break out the while loop when "Lookup failed" happened in the program
do
echo "not failed" # do something
$x = "Lookup failed" # just for testing the break-out
INTERVAL=(( $INTERVAL + 10 )); # the interval increments by 10
done
echo $x
echo $INTERVAL
But this shell script is not working, with this error:
./test.sh: line 9: x: command not found
./test.sh: line 12: [[: command not found
Could someone help me please? I appreciate your help.

You need spaces around the [ command name. You also need a space before the ] argument at the end of the command.
You also cannot have spaces around assignments in shell. And your assignment in the loop does not need a $ at the start.
x="Lookup success"
INTERVAL=0 # count the number of runs
while [ "$x" != "Lookup failed" ]
do
echo "not failed"
x="Lookup failed"
INTERVAL=(( $INTERVAL + 10 ))
done
echo $x
echo $INTERVAL

Not sure if there's a shell that would accept INTERVAL=((...)); my version of ksh and bash on two platforms does not. INTERVAL=$((...)) does work:
#!/bin/bash
x="Lookup success"
INTERVAL=0 # count the number of runs
while [ "$x" != "Lookup failed" ]
do
echo "not failed"
x="Lookup failed"
INTERVAL=$(( $INTERVAL + 10 ))
done
echo $x
echo $INTERVAL
Credits go to #JonathanLeffler. I'll appreciate up-votes so that next time I don't have to copy-paste others' solution for pointing out a simple typo (comment rights start with rep>=50).

Add a space after [ and before ].
Also, as Jonathan said, you cannot have space in assignments as well.

Related

Bash Script stuck looping

I'm trying to write a script that runs another script which fails rarely until it fails.
Here is the script that fails rarely:
#!/usr/bin/bash
n=$(( RANDOM % 100 ))
if (( n == 42 )) ; then
echo "$n Something went wrong"
>&2 echo "The error was using magic numbers"
exit 1
fi
echo "$n Everything went according to plan"
Here is the script that should run the previous script until it fails:
#!/usr/bin/bash
script_path="/tmp/missing/l2q3.sh"
found=0
counter=0
while (( $found == 0 )); do
output=(bash $script_path)
if (( $output == 42 Something went wrong )); then
found=1
fi
((counter++))
if (( $found == 1 )); then
echo "Number 42 was found after $counter tries"
fi
done
when I try running the second script I get stuck in an infinite loop saying there is a syntax error on line 11 and that something is wrong with 42 Something went wrong. I've tried with "42 Something went wrong" aswell and still stuck in a loop.
The form (( )) is arithemetic only, so you cannot test a string inside.
To test a string, you have to use the [[ ]] version:
[[ $output == "42 Something went wrong" ]] && echo ok
ok
You can use the program execution as the test for a while/until/if (etc.)
Assuming your script returns a valid 0 error code on success, and nonzero on any other circumstance, then -
$: cat tst
#!/bin/bash
trap 'rm -fr $tmp' EXIT
tmp=$(mktemp)
while /tmp/missing/l2q3.sh >$tmp; do let ++ctr; done
grep -q "^42 Something went wrong" $tmp &&
echo "Number 42 was found after $ctr tries"
In use:
$: ./tst
The error was using magic numbers
Number 42 was found after 229 tries
here are 3 steps to move forward.
add a return value at the end of the first script
exit 0
make your first script has executable rights
$ chmod a+x /tmp/missing/12q3.sh
instead of while loop you may use until, which would run until it returns success i.e. 0
until /tmp/missing/l2q3.sh; do ((counter++)) done
for other if statements please use square brackets [ single or double [[.

How to use a for loop to create folders in bash

I want to create a directory in which there is a bunch of text files using a for loop. Here is my code:
#!/bin/bash
echo "enter the nums: "
read num1 num2
for (( counter=0; counter<$num2; counter++ ))
do
if [ $num1 -lt 10 ] && [ $num2 -lt 10 ];
then
mkdir $num1 && touch $num1/$num1$num2.txt
echo "$num1""$num2" > $num1/$num1$num2.txt
else
echo "you weren't supposed to do that"
fi
done
What I want to happen if for example the user entered: "2 9"
Make a directory called 2
In it make text files called 290.txt, 291.txt, 292.txt... up till 299.txt.
Instead, what happens right now is it makes the directory and gives an error that the directory already exists. I don't know the next step, please help.
The biggest problem here is that you're doing things inside the loop that really only should be done once. Specifically, the error you're getting is because it tries to create the directory every time through the loop, but you can only create it once. Also, if the user enters too large a number, it'll print multiple error messages (e.g. if num2 is entered as 500, it'll print 500 error messages). You need to do both the error check and creating the directory once, before the loop.
A second problem is that you don't add $counter to the filename, so if the user enters "2 9", it'll create a file named 29.txt nine times.
You also have some more minor issues: in general, error messages should be printed to standard error instead of standard output (you can redirect them with >&2), and if there's an error the script should exit with a nonzero status. Also, you should (almost always) put double-quotes around variable references, to avoid weird results if the variables are blank or contain whitespace or some other things. You also don't need to touch files before writing into them (using > somefile will create the file if it doesn't exist).
With these things fixed (and some stylistic tweaks), here's what I get:
#!/bin/bash
echo "enter the nums: "
read num1 num2
if ! [ "$num1" -lt 10 ] || ! [ "$num2" -lt 10 ]; then
echo "you weren't supposed to do that" >&2 # message send to stderr
exit 1 # exit with error status
fi
mkdir "$num1" || exit $? # if mkdir fails, exit with its error status
for (( counter=0; counter<$num2; counter++ )); do
echo "${num1}${num2}" > "${num1}/${num1}${num2}${counter}.txt"
done
BTW, the ! [ "$num1" -lt 10 ] tests may look a little weird; why not just use [ "$num" -ge 10 ]? I did it that way in case $num1 and/or $num2 isn't a valid number, in which case both -lt and -ge tests would fail; using a negated test makes that an error rather than a success.
I'm not fluent in bash or anything, but it looks like mkdir $num1 is called on every loop. Find out first if the directory exists.
Here you are! change the if and for statement parent and child:
#!/bin/bash
echo "enter the nums: "
read num1 num2
if [ $num1 -lt 10 ] && [ $num2 -lt 10 ]; then
mkdir $num1
for i in $(seq 0 $num2); do
touch $num1/$num1$num2$i.txt
echo "$num1""$num2""$i" > $num1/$num1$num2$i.txt
done
else
echo "you weren't supposed to do that"
fi

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"

No output from script

I've edited my script, and get no more errors, however, the script is not executing to the Minecraft server, no announcement attempts are made at all for that matter. I'm rally puzzled. It's as if it's not running at all like the server is not running, but it is, and should be matching "is running" from the status command.
and code is:
#!/bin/bash
checkServer=$(/etc/init.d/minecraft status);
cd /.smc;
# Is the server even running?
if [ checkServer = *"is running"* ];
then
# No count file? Create it.
if [ ! -f /.smc/lastAnnouncement.txt ];
then
echo 0 < /.smc/lastAnnouncement.txt;
fi
# Load count
lastAnn=$(cat /.smc/lastAnnouncement.txt);
# ANNOUNCEMENTS
announcement[0]='Dont forget to check out http://fb.com/pyrexiacraftfans for news and updates';
announcement[1]='Use our Facebook page to request land protection! Visit http://fb.com/pyrexiacraftfans';
# Should we restart announcement que?
if lastAnn == ${#announcement[#]}+1;
then
echo 0 < /.smc/lastAnnouncement.txt;
fi
# Send announcement
sendAnnouncement=$(/etc/init.d/minecraft command say announcement[lastAnn]);
# Next announcement count
lastAnn=$((lastAnn+1));
# Write next announacment count
echo lastAnn < /.smc/lastAnnouncement.txt;
fi
There are multiple issues with your script, ranging from useless semicolons to bad logic. The list of issues is so long that it's easier to post a corrected script than point out the issues (the other answers don't even come close to listing all the errors).
The corrected script is:
#!/bin/bash
checkServer=$(/etc/init.d/minecraft status)
cd /.smc
# Is the server even running?
if [[ $checkServer =~ "is running" ]]; then
# No count file? Create it.
if [ ! -f /.smc/lastAnnouncement.txt ]; then
echo 0 > /.smc/lastAnnouncement.txt
fi
# Load count
lastAnn=$(cat /.smc/lastAnnouncement.txt)
# ANNOUNCEMENTS
announcement[0]='Dont forget to check out http://fb.com/pyrexiacraftfans for news and updates'
announcement[1]='Use our Facebook page to request land protection! Visit http://fb.com/pyrexiacraftfans'
# Send announcement
sendAnnouncement=$(/etc/init.d/minecraft command say ${announcement[$lastAnn]})
# Next announcement count
((++lastAnn))
# Write next announacment count
# Should we restart announcement que?
if [[ $lastAnn -gt ${#announcement[#]} ]]; then
echo 0 > /.smc/lastAnnouncement.txt
else
echo $lastAnn > /.smc/lastAnnouncement.txt
fi
fi
The issues with your script (leaving aside the excess semicolons which don't hurt, just needless disk space wastage):
missing $ before variable name
Incorrect string comparison. Use =~ instead of ==, [[ instead of [, and remove * from both sides of the string *"is running"*
if [ checkServer == *"is running"* ]
Wrong redirection. you want to write to file, so >, not <. This is multiple times.
echo 0 < /.smc/lastAnnouncement.txt;
echo 0 < /.smc/lastAnnouncement.txt;
Variable names missing $ and wrong redirection
echo lastAnn < /.smc/lastAnnouncement.txt;
Easier increment with ((++lastAnn)). Also this is invalid shell, as arithmetic needs expr command or ((...)) builtin
lastAnn=$lastAnn+1;
Missing $ in variable name. Missing test , [ or [[. Missing expr or $((..)) for addition of 1. -eq should be used instead of == for number equality. Logically this should use -gt to test against last index and +1 is not required.
if lastAnn == ${#announcement[#]}+1;
I won't go into the fact that the logic of writing the message queue index was incorrect, and would never loop back to 0.
However, you did a wonderful job of trying to write a script. Many people don't even try.
Edit : I missed out a {} on the array variable usage on line 21 of the script above. Fixed.
Try:
if [ checkServer = *"is running"* ];
(Yes single equals sign)
There are many errors:
First,
if [[ $checkserver == *"is running"* ]]
Using double [[...]] and a variable reference is $checkserver.
Then,
sendAnnouncement=$(
Without space.
Also,
if [ $lastAnn == $((${#announcement[#]}+1)) ]
Probably more...
I believe your script has quite a few syntax error.
i.e. there is a problem in this line:
sendAnnouncement = $(/etc/init.d/minecraft command say $announcement[$lastAnn]);
Replace it with this:
sendAnnouncement=$(/etc/init.d/minecraft command say $announcement[$lastAnn])
bash (and other shells) doesn't allow spaces before and after the assignment operator =
Also this line:
lastAnn=$lastAnn+1;
should be replaced with:
lastAnn=$((lastAnn+1))

Bash Error - Program errors on the lines with the double semi-colons

Program errors on the lines with the double semi-colons, stating a Syntax error near unexpected token. Any suggestions?
#!/bin/bash
echo "Enter the access password..."
while
do
read INPUT_STRING
case $INPUT_STRING in
##CORRECT PASSWORD##
lux)
ls -l -S > directory.txt
echo "Enter your username..."
read a
sed '1 i\ $a' directory.txt
date=`date`
sed '2 i\ $date' directory.txt
date=
echo "The operation has completed successfully"
;;
##INCORRECT PASSWORD##
*)
x=1
while [ $x -le 3 ]
do
echo "Incorrect Password, try again. The program will exit after 3 failed attempts."
x=$(( $x + 1 ))
sleep 2
echo "Enter the access password..."
if x=3
then exit
fi
;;
esac
done
echo
echo "Process Complete, Goodbye!"
Your while syntax is messed up. You need a condition between the while and the do. That's probably screwing up the parsing of the case statement.
There is a syntax error in your code. You need to provide an expression after while.
Right now, you have:
while
do
and you need to specify the epxression that the while keyword will loop on. More specifically, it looks like you just want an infinite loop. If this is true, you need to specify:
while true
do
You seem to be lacking a done to close the while statement in the *) case

Resources