integer expected error...blank line? - bash

I'm getting a "integer expression expected error" when doing the following:
NM=$(<file)
if test $NM -gt 0
then
echo "workflow 1 would follow here"
else
if test $NM -lt 0
then
echo "workflow 2 would go after this"
else
echo "something else"
fi
fi
The file from which I am getting $NM assigned only contains one number (always an integer). This file is the output of a gmtmath operation.
I've noticed that if I open that file it has an extra line below the line containing the number and, if I manually delete that empty line my loop works and I don't get the error. However, I've tried to use sed in various ways to automatically delete empty lines and it deletes the entire content of the file, including the number. Any ideas??

After reading some comments, I'm wondering if you are getting some unprintable characters. I've tested this by putting in a \r before the \n, was finally able to reproduce. Here's a cleaned up version of your code, using tr to remove some extra characters.
NM=$(tr -cd '[:graph:]' <file)
if [[ $NM -gt 0 ]]; then
echo "workflow 1 would follow here"
elif [[ $NM -lt 0 ]]; then
echo "workflow 2 would go after this"
else
echo "something else"
fi
I could have used other classes other than [:graph:]. I just end up using [:graph:] often, but to each their own.

You want to test ${#NM} is greater than zero (e.g. the number of characters in $NM). Currently you are testing the contents of the string against 0 and bash is telling you it needs a number. The same applies for each of your tests where you need a number.
If you want to test whether the word "$NM" is greater than 0 or less than 0 use if ((NM > 0)); then ... or if ((NM < 0)); then ....
You can also use the older test expression if [ "$NM" -gt 0 ]; then... or if test "$NM" -gt 0 ; then... just be aware you will generate your error if "$NM" cannot be interpreted as a number. (you can redirect stderr to avoid that, but that isn't wise here).

Related

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

Multiple conditions in while loop failing because of input type in Bash, or 3 digit number condition required

I am trying to control the user input to a script so that a only 3 digit number can pass. I started with two ifs and these work fine.
echo -e "Input sequence number:"
read SEQUENCE
NUMTEST='^[0-9]+$'
if ! [[ $SEQUENCE =~ $NUMTEST ]]; then
echo "ERROR:" "$SEQUENCE" "is not a number! Try again!"
exit 1
fi
SEQLEN=$(printf "%s" "$SEQUENCE" | wc -c)
if (($SEQLEN != 3)); then
echo "ERROR:" "$SEQUENCE" "is not a 3 digit number! Try again!"
exit 1
fi
A nicer solution would be to use a while loop so the user doesn't have to keep re-running the script, but whilst I can get a while loop to work for each individual condition, when I combine them a text input kills the loop as the numerical condition doesn't like it.
echo -e "Input sequence number:"
read SEQUENCE
NUMTEST='^[0-9]+$'
SEQLEN=$(printf "%s" "$SEQUENCE" | wc -c)
while ! [[ $SEQUENCE =~ $NUMTEST ]] && (($SEQLEN != 3)); do
echo "ERROR:" "$SEQUENCE" "is not a 3 digit number! Try again!"
echo -e "Input sequence number:"
read SEQUENCE
SEQLEN=$(printf "%s" "$SEQUENCE" | wc -c)
done
I'm thinking either I need to create a single condition $SEQUENCE == 3 digit number or find a way to suppress the errors such that any error mean the loop continues.
To be explicit my questions are:
Is there a way to write the condition $SEQUENCE == 3 digit number?
Can i suppress the error and still go to the loop, or is this a script terminating error?
Suggestions and improvements much appreciated. This is my first script so I won't be surprised if I'm committing some poor practices so any other recommendations appreciated.
Is there a way to write the condition $SEQUENCE == 3 digit number?
Yes. Change your regex to ^[0-9]{3}.
With a small trick you can also remove the duplicated input processing:
while true; do
read -p 'Input sequence number: ' sequence
[[ "$sequence" =~ ^[0-9]{3}$ ]] && break
echo "ERROR: '$sequence' is not a 3 digit number!"
done
Don't you mean:
while ! [[ $SEQUENCE =~ $NUMTEST ]] || (($SEQLEN != 3)); do
You need to loop again when not a number or not three digit (which is different from your construct, where you loop when not a number and length is not three).
Also, you can create a regexp for amtching three-digit numbers, as stated in #Socowi's answer.

Monitoring script does not output anything

Can anybody tell me what's wrong in this script, it's not working. When I run it, there is no output/error on the screen.
The script is to monitor a log file to check the value of one of the columns, and if it is more than 20 it will echo a message.
#!/bin/bash
while true ; do
COUNT=`tail -f /monitoring/log.20160121|cut -d" " -f39`
echo $COUNT
if [ $COUNT -gt 20 ] ;then
echo "Count is high"
break
fi
sleep 10
done
tail -f does not exit, so your script gets stuck there. I assume you are just interested in the last line of the log; tail -n 1 does that.
Other points:
Indentation: not sure how much got lost while copy pasting, but proper indentation massively increases readability of your code
Variable names: all uppercase variable names are discouraged as they might clash with reserved (environment) variable names
Command substitution with backticks (` `) is discouraged and the form $( ) is preferred; makes for example nesting easier
Since you're using Bash, you can use the (( )) conditional construct, which is better suited for comparing numbers than [ ]
Together:
#!/bin/bash
while true; do
count=$(tail -n 1 /monitoring/log.20160121 | cut -d " " -f 39)
echo $count
if (( count > 20 )); then
echo "Count is high"
break
fi
sleep 10
done

solaris simple bash script

I'm trying to execute this simple script in solaris.
I want to sort(numeric) the filenames of the files in source directory and copy the file one by one to another directory. And, I want to print a message after copying every 100 files.
#!/bin/bash
count=0
for i in `ls | sort -n`
do
cp $i ../target
count = $((count+1))
if[ $count%100 -eq 0 ]
then
echo $count files copied
sleep 1
fi
done
this is not working. I tried different things after searching in net.
I get errors like these -
syntax error at line 8: '(' unexpected.
syntax error at line 10: 'then' unexpected.
syntax error at line 13: 'fi' unexpected etc.
What is the problem with this script?
bash version - GNU bash, version 3.00.16(1)-release (sparc-sun-solaris2.10)
The basic problem with the script is spacing. You have spaces where you shouldn't have them:
(wrong) count = $((count+1))
(right) count=$((count+1))
(better) ((count++))
and you're missing spaces where you need them:
(wrong) if[ $count%100 -eq 0 ]
(right) if [ $((count % 100)) -eq 0 ]
(better) if ((count % 100 == 0))
count = $((count+1)) tries to run the command count passing it two arguments, = and the value of count+1. if[ ... tries to run the command if[ because [ is a valid word character; it doesn't automatically start a new token.
Having said all that, I'm puzzled by the unexpected ( error message. Could your bash be too old to recognize $(( syntax? Possibly. It's a very old bash.
count=$((count+1))
if [ `echo $count % 100 | bc` -eq 0 ]
Make these corrections.
Edit: Please try
count=`expr $count + 1`
I see a few errors here. First, you need double quotes around $i in case they have special characters.
Second, you shouldn't ever use
for i in `ls | sort -n`
Instead, try the following
ls -1 | sort -n | while read i
Third, change your if statement to
if ((count%5 == 0))
The (( syntax is bash is made just for integer math.
Fourth, change your counter increment to
((count++))
This is more concise. Also, the space in your version may break things. Remember, spaces matter.

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

Resources