2

I'm trying to write a bash script. The script should check if the MC server is running. If it crashed or stopped it will start the server automatically.

I'll use crontab to run the script every minute. I think I can run it every second it won't stress the CPU too much. I also would like to know when was the server restarted. So I'm going to print the date to the "RestartLog" file.

This is what I have so far:

   #!/bin/sh

ps auxw | grep start.sh | grep -v grep > /dev/null

if [ $? != 0 ]
then
        cd /home/minecraft/minecraft/ && ./start.sh && echo "Server restarted on: $(date)" >> /home/minecraft/minecraft/RestartLog.txt > /dev/null
fi

I'm just started learning Bash and I'm not sure if this is the right way to do it.

James
  • 4,644
  • 5
  • 37
  • 48
Doctor Who
  • 23
  • 2
  • 1
    Trying to avoid this being closed as opinionated, "how would you do it" and "is this the right way" are borderline opinionated as we'd all do it differently. It's preferred to help you with your code and style. So does your code work? What specifically does it not do that you'd like etc? – James May 09 '18 at 13:39
  • 1
    I would strongly recommend you write this as: `if ! ps ....; then ...; fi`. Do not explicitly check `$?`; it is almost never necessary, and scripts are much cleaner if you just check the command status directly. – William Pursell May 09 '18 at 13:51
  • 1
    A second recommendation is; don't do this. Rather than spawning a job from cron that does a (very fragile) check if your server is running, run the server under the supervision of systemd or supervisord. – William Pursell May 09 '18 at 13:55
  • `$?` is an integer and should be treated as such inside the `[ … ]`, which means you should use `[ $? -ne 0 ]`. See `man test`, since `[` is actually just an alias for the program called `test`. – Robin479 May 09 '18 at 13:58
  • Your echo-log command defines 2 output redirections, which is an error: The first one is `>> …/RestartLog.txt` and the second one is `> /dev/null`. Since a program only has one standard-output only one redirection can be in effect. The shell will actually apply both in turns, but only the last one wins, so the log message gets sent to `/dev/null`. You probably wanted the second output-redirection to redirect the standard-error to `/dev/null` which would be written as `2> /dev/null`, but this is actually useless, because `echo` never produces any output on standard-error (unless magic). – Robin479 May 09 '18 at 14:08
  • @James It doesn't matter if `0` is quoted or not, but since you are using `-ne` to compare two integer values for non-equality (value-wise) the proper form should be `[ $? -ne 0 ]`. – Robin479 May 09 '18 at 14:12
  • @James What should be the difference? In both cases the string `0` is passed to the `test` command, except that in one case it is explicitly quoted (and unquoted by the shell before passing it to `test`), which is unnecessary for a simple string like `0`. – Robin479 May 09 '18 at 14:22
  • Ideally, `start.sh` should be intelligent enough not to start the process if one is already running. – codeforester May 09 '18 at 14:33
  • Thank you for the comments. I'm going to check all the recommendations. – Doctor Who May 09 '18 at 15:04
  • @James Quoting *parameter expansions* is important; there is no difference between the bare literal `0`, `"0"`, or `'0'`. `[` receives the string `0` in all three cases. – chepner May 09 '18 at 15:19
  • Robin479 & @chepner https://stackoverflow.com/a/13086180/2632129 I just feel it's prudent to do it as a habit. Another debate for another time though :) – James May 09 '18 at 15:22
  • Doing so out of habit is quite different from *needing* to do so. – chepner May 09 '18 at 15:25

3 Answers3

0

You can use monit for this task. See docu. It is available on most linux distributions and has a straightforward config. Find some examples in this post

For your app it will look something like

check process minecraftserver
    matching "start.sh"
    start program = "/home/minecraft/minecraft/start.sh"
    stop program = "/home/minecraft/minecraft/stop.sh"

I wrote this answer because sometimes the most efficient solution is already there and you don't have to code anything. Also follow the suggestions of William Pursell and use the init system of your OS (systemd,upstart,system-v,etc.) to host your scripts.

Find more: Shell Script For Process Monitoring

jschnasse
  • 8,526
  • 6
  • 32
  • 72
  • I didn't down vote your answer. It was someone else. I appreciate your answer! I'll check monit. – Doctor Who May 09 '18 at 15:09
  • 1
    Now you have added more than links, I've removed the downvote. Link only answers are a bit pointless ;) – James May 09 '18 at 15:24
0

If start.sh blocks until the server exists/crashes, you'd be better off to simply restart it in an infinite loop without the involvement of cron. Simply type in a console (or put into another script):

#!/bin/bash
cd /home/minecraft/minecraft/
while sleep 3; do
  echo "$(date) server (re)start" >> restart.log
  ./start.sh # blocks until server crashes
done

But if it doesn't block (i.e. if start.sh starts the server and then returns, but the server keeps running), you would need to implement a different check to verify if the server is actually still running, other than ps|grep start.sh

PS: To kill the infinite loop you have to Ctrl+C twice: Once to stop ./start.sh and once to exit from the immediate sleep.

Robin479
  • 1,606
  • 15
  • 16
  • Thank you for your answer. I'm running the server in a screen. The screen name is "minecraft". So whenever the server crashes it will start in the existing "minecraft" screen. Probably this is a safest way to not start the server multiple times. – Doctor Who May 09 '18 at 15:06
0

The use of cron is possible, there are other (better) solutions (monit, supervisord etc.). But that is not the question; you asked for "the right way". The right way is difficult to define, but understanding the limits and problems in your code may help you.

Executing with normal cron will happen at most once per minute. That means that you minecraft server may be down 59 seconds before it is restarted.

   #!/bin/sh

You should have the #! at the beginning of the line. Don't know if this is a cut/paste problem, but it is rather important. Also, you might want to use #!/bin/bash instead of #!/bin/sh to actually use bash.

ps auxw | grep start.sh | grep -v grep > /dev/null

Some may suggest to use ps -ef but that is a question of taste. You may even use ps -ef | grep [s]tart.sh to prevent using the second grep. The main problem however with this line is that that you are parsing the process-list for a fairly generic start.sh. This may be OK if you have a dedicated server for this, but if there are more users on the server, you run the risk that someone else runs a start.sh for something completely different.

if [ $? != 0 ]
then

There was already a comment about the use of $? and clean code.

    cd /home/minecraft/minecraft/ && ./start.sh && echo "Server restarted on: $(date)" >> /home/minecraft/minecraft/RestartLog.txt > /dev/null

It is a good idea to keep a log of the restarts. In this line, you make the execution of the ./start.sh dependent on the fact that the cd succeeds. Also, the echo only gets executed after the ./start.sh exists.

So that leaves me with a question: does start.sh keep on running as long as the server runs (in that case: the ps-test is ok, but the && echo makes no sense, or does start.sh exit while leaving the minecraft-server in the background (in that case the ps-grep won't work correctly, but it makes sense to echo the log record only if start.sh exits correctly).

fi

(no remarks for the fi)

Ljm Dullaart
  • 4,273
  • 2
  • 14
  • 31
  • he uses `grep -v`. This will return only 0 if the output includes a line that does not contain 'grep'. So the `if` part should **not** be a problem. Check it `echo grep|grep -v grep ;echo $?` gives `1` while `echo -e "grep\nsomething else"|grep -v grep ;echo $?` gives `0` – jschnasse May 11 '18 at 07:26
  • You are absolutely correct. My mistake. I removed the remark about the `grep -v`. – Ljm Dullaart May 11 '18 at 08:53