6
\$\begingroup\$

I've been working on ubuntu since many years but not quite familiar with bash scripting. My development tools involve servers like nginx and apache, the mysql database and beanstalkd. Obviously, I just can't keep all these services running always and overwhelm my CPU capacity!, so I need a way of starting a bunch of these services without having to write poetry like:

sudo service apache2 start
sudo service mysql start
..blah blah..

Hence I've written this script called kick (no laughs!), so I can just pass it some parameters and say:

kick start apache2 mysql
# OR
kick stop nginx php-fpm

Since the starting mechanism of each of these services are different, I had to deal each one in a separate if condition. I would like to know from you bash experts whether this is the correct way to do it:

filename: kick

#!/bin/bash
function docmd()
{
 #echo 'func',"1ドル","2ドル"
 if [ "1ドル" = "nginx" ];
 then
 if [ "2ドル" = "sh" ]; then
 /home/prahlad/programs/nginx-1.6.0/sbin/nginx -s stop
 elif [ "2ドル" = "re" ]; then
 /home/prahlad/programs/nginx-1.6.0/sbin/nginx
 sleep 1
 /home/prahlad/programs/nginx-1.6.0/sbin/nginx -s stop
 else
 /home/prahlad/programs/nginx-1.6.0/sbin/nginx
 fi
 elif [ "1ドル" = "php-fpm" ]
 then
 if [ "2ドル" = "sh" ]; then
 pkill php-fpm
 elif [ "2ドル" = "re" ]; then
 pkill php-fpm
 sleep1
 /home/prahlad/programs/php-5.4/sbin/php-fpm
 else
 /home/prahlad/programs/php-5.4/sbin/php-fpm
 fi
 elif [ "1ドル" = "apache2" ]
 then
 if [ "2ドル" = "sh" ]; then
 sudo service apache2 stop
 elif [ "2ドル" = "re" ]; then
 sudo service apache2 restart
 else
 sudo service apache2 start
 fi
 elif [ "1ドル" = "mysql" ]
 then
 if [ "2ドル" = "sh" ]; then
 sudo service mysql stop
 elif [ "2ドル" = "re" ]; then
 sudo service mysql restart
 else
 sudo service mysql start
 fi
 else
 echo "Unrecognized verb",1,ドル2ドル
 fi
}
cmd=""
if [ "1ドル" == "restart" ]; then
 cmd="re"
elif [ "1ドル" == "stop" ]; then
 cmd="sh"
elif [ "1ドル" == "start" ]; then
 cmd="st"
fi
for name in $@
do
 if [ "$name" != "start" ] && [ "$name" != "stop" ] && [ "$name" != "restart" ]; then
 docmd $name $cmd
 fi
done
janos
113k15 gold badges154 silver badges396 bronze badges
asked Jun 24, 2014 at 2:18
\$\endgroup\$

3 Answers 3

7
\$\begingroup\$

Don't Repeat Yourself

You start/stop/restart apache2 and mysql the same way, so you don't need to duplicate their code. You could create a generalized function that can handle these or any standard service well, like this:

startstop_service() {
 cmd=1ドル
 name=2ドル
 sudo service $name $cmd
}

Naming

You renamed the universal and intuitive command names start/stop/restart to the shorter but kind of obscure st/sh/re. I recommend to stick with the originals. Among other things, the startstop_service in my previous point will work out of the box.

The case statement

The case statement in Bash is more concise and easier to read than many if-elif-elif-else statements, for example:

case "1ドル" in
 start|stop|restart) cmd=1ドル ;;
 *) echo "usage: 0ドル [start|stop|restart] servicenames"; exit 1
esac
shift

Minor things

  • Your script doesn't use any Bash specific features, /bin/sh would be enough
  • It's better to validate the cmd parameter once, instead of for every single service name as in your code
  • If the cmd is invalid, it's good to exit the script with a non-zero exit code (typically exit 1), to indicate failure
  • You can simplify for name in "$@"; do as for name; do

Suggested implementation

Here's an alternative implementation using the above suggestions:

#!/bin/sh -e
NGINX=/home/prahlad/programs/nginx-1.6.0/sbin/nginx
PHP=/home/prahlad/programs/php-5.4/sbin/php-fpm
PHP_NAME=$(basename $PHP)
startstop_service() {
 cmd=1ドル
 name=2ドル
 sudo service $name $cmd
}
startstop_nginx() {
 cmd=1ドル
 case $cmd in
 stop) $NGINX -s stop ;;
 start) $NGINX ;;
 restart)
 $NGINX -s stop
 sleep 1
 $NGINX
 ;;
 esac
}
startstop_php() {
 cmd=1ドル
 case $cmd in
 stop) pkill $PHP_NAME ;;
 start) $PHP ;;
 restart)
 pkill $PHP_NAME
 sleep 1
 $PHP
 ;;
 esac
}
case "1ドル" in
 start|stop|restart) cmd=1ドル ;;
 *)
 shift
 servicenames=${@-servicenames}
 echo "usage: 0ドル [start|stop|restart] $servicenames"
 exit 1
esac
shift
for name; do
 case "$name" in
 php-fpm) startstop_php $cmd ;;
 nginx) startstop_nginx $cmd ;;
 *) startstop_service $cmd $name ;;
 esac
done

Notice that apache2 and mysql are not even mentioned in this script, they are automatically handled by the generic startstop_service function. The script will work with any other standard service too.

As @vnp pointed out, the sleep 1 calls are dodgy, I did not fix that. To fix that, look for pid files created by those services, and use them to check if the service is still running or not.

The bigger picture

Ideally, you should standardize everything: make it possible to start/stop/restart your custom php-fpm and nginx just like other services. That way you can remove the service-specific logic from this script (doesn't really belong here anyway), and then it can work for any service you throw at it. Unfortunately I don't know enough Ubuntu to guide you, but I'm sure the guys on askubuntu.com can help with that.

answered Jul 6, 2014 at 9:50
\$\endgroup\$
2
  • \$\begingroup\$ This is definitely the better implementation, it leaves more room for extensibility; would you want *) echo -e "usage: 0ドル [start|stop|restart] 2ドル"; exit 1 instead? \$\endgroup\$ Commented Mar 28, 2016 at 16:27
  • \$\begingroup\$ @mynameismevin you mean, replacing servicenames in my version with 2ドル? Not so easy: there can be multiple service names there, 3ドル, 4ドル, ... Or there might be none at all. So I improved it, to handle all those cases. By the way, avoid flags of echo such as -e, it's not portable. (In the example you gave, it has no purpose anyway.) \$\endgroup\$ Commented Mar 28, 2016 at 16:42
3
\$\begingroup\$

Addressed below are purely coding problems.

  • Do not spell out same path many times. Define it once (e.g. NGINX=/home/prahlad/programs/nginx-1.6.0/sbin/nginx), and refer to it as necessay (e.g. $NGINX)

  • Error message in docmd is misleading: it tells that a verb is wrong. Yet the error condition is a wrong noun.

  • I don't see a need to rename the verb. docmd can deal as easily with stop as with sh.

  • sleep 1 is a gamble. You need a more reliable way to ensure that a process is really gone, and to deal with those which do not go peacefully.

  • The nginx restart seems to be messed up.

  • Once you validate 1ドル as a known command, you could just shift it, instead of testing an argument in each iteration.

answered Jun 24, 2014 at 7:49
\$\endgroup\$
0
\$\begingroup\$

See Bash Service Manager project: https://github.com/reduardo7/bash-service-manager

Implementation example

#!/usr/bin/env bash
export PID_FILE_PATH="/tmp/my-service.pid"
export LOG_FILE_PATH="/tmp/my-service.log"
export LOG_ERROR_FILE_PATH="/tmp/my-service.error.log"
. ./services.sh
run-script() {
 local action="1ドル" # Action
 while true; do
 echo "@@@ Running action '${action}'"
 echo foo
 echo bar >&2
 [ "$action" = "run" ] && return 0
 sleep 5
 [ "$action" = "debug" ] && exit 25
 done
}
before-start() {
 local action="1ドル" # Action
 echo "* Starting with $action"
}
after-finish() {
 local action="1ドル" # Action
 local serviceExitCode=2ドル # Service exit code
 echo "* Finish with $action. Exit code: $serviceExitCode"
}
action="1ドル"
serviceName="Example Service"
serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish

Usage example

$ ./example-service
# Actions: [start|stop|restart|status|run|debug|tail(-[log|error])]
$ ./example-service start
# Starting Example Service service...
$ ./example-service status
# Serive Example Service is runnig with PID 5599
$ ./example-service stop
# Stopping Example Service...
$ ./example-service status
# Service Example Service is not running
answered Jun 3, 2017 at 1:21
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.