Shell script create unexpected file "start" on starting process - bash

I've found and modified a simple shell script to start/stop a jar, but when launching the script it creates an extra empty start file.
I cannot understand why. Any clue?
#!/bin/bash
case $1 in
start)
if [[ -e myprog.pid ]]
then
echo "myprog.pid found. Is myprog already running?"
else
exec java -jar myprog-0.0.1-SNAPSHOT.jar 1>/dev/null 2>$1 &
echo $! > myprog.pid;
fi
;;
stop)
kill $(cat myprog.pid);
rm myprog.pid
;;
*)
echo "usage: myprog {start|stop}" ;;
esac
exit 0

Your problem is 2>$1. That's a typo.
You meant 2>&1.
What you wrote is expanded by the shell as 2>start and creates your file.

Related

Is there any better way to run a script as daemon which continuously polls a directory to check the presence of a file?

I have to continuously check if a file is present in a particular directory. I am doing this with filecopy.sh script:
#!/bin/bash
while true;
do
if [ -f /var/tmp/*.*cim ]; then
echo "Checking the file available in the path"
mv /var/tmp/*.*cim /etc/opt/maptranslator/ss7
/etc/init.d/ss7-stack restart
else
continue;
fi
done
I want the filecopy.sh script to run as daemon. I wrote the following script:
#!/bin/bash
case "$1" in
start)
/etc/init.d/filecopy.sh &
echo $!>/var/run/filecopy.pid
;;
stop)
kill `cat /var/run/filecopy.pid`
rm /var/run/filecopy.pid
;;
restart)
$0 stop
$0 start
;;
status)
if [ -e /var/run/filecopy.pid ]; then
echo filecopy.sh is running, pid=`cat /var/run/filecopy.pid`
else
echo filecopy.sh is NOT running
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|status|restart}"
esac
exit 0
I would like to know if there is any better way to achieve this.
Write a small C program that calls inotify(7).
See http://man7.org/linux/man-pages/man7/inotify.7.html or man 7 inotify
In this case you are waiting for /var/tmp to change.
However you really really shouldn't be using /var/tmp at all unless you want some random user to hose your protected area. File a security bug against the process that created its pid file in /var/tmp.

Simple bash script for starting application silently

Here I am again. Today I wrote a little script that is supposed to start an application silently in my debian env.
Easy as
silent "npm search 1234556"
This works but not at all.
As you can see, I commented the section where I have some troubles.
This line:
$($cmdLine) &
doesn't hide application output but this one
$($1 >/dev/null 2>/dev/null) &
works perfectly. What am I missing? Many thanks.
#!/bin/sh
# Daniele Brugnara
# October, 2013
# Silently exec a command line passed as argument
errorsRedirect=""
if [ -z "$1" ]; then
echo "Please, don't joke me..."
exit 1
fi
cmdLine="$1 >/dev/null"
# if passed a second parameter, errors will be hidden
if [ -n "$2" ]; then
cmdLine="$cmdLine 2>/dev/null"
fi
# not working
$($cmdLine) &
# works perfectly
#$($1 >/dev/null 2>/dev/null) &
With the use of evil eval following script will work:
#!/bin/sh
# Silently exec a command line passed as argument
errorsRedirect=""
if [ -z "$1" ]; then
echo "Please, don't joke me..."
exit 1
fi
cmdLine="$1 >/dev/null"
# if passed a second parameter, errors will be hidden
if [ -n "$2" ]; then
cmdLine="$cmdLine 2>&1"
fi
eval "$cmdLine &"
Rather than building up a command with redirection tacked on the end, you can incrementally apply it:
#!/bin/sh
if [ -z "$1" ]; then
exit
fi
exec >/dev/null
if [ -n "$2" ]; then
exec 2>&1
fi
exec $1
This first redirects stdout of the shell script to /dev/null. If the second argument is given, it redirects stderr of the shell script too. Then it runs the command which will inherit stdout and stderr from the script.
I removed the ampersand (&) since being silent has nothing to do with running in the background. You can add it back (and remove the exec on the last line) if it is what you want.
I added exec at the end as it is slightly more efficient. Since it is the end of the shell script, there is nothing left to do, so you may as well be done with it, hence exec.
& means that you're doing sort of multitask whereas
1 >/dev/null 2>/dev/null
means that you redirect the output to a sort of garbage and that's why you don't see anything.
Furthermore cmdLine="$1 >/dev/null" is incorrect, you should use ' instead of " :
cmdLine='$1 >/dev/null'
you can build your command line in a var and run a bash with it in background:
bash -c "$cmdLine"&
Note that it might be useful to store the output (out/err) of the program, instead of trow them in null.
In addition, why do you need errorsRedirect??
You can even add a wait at the end, just to be safe...if you want...
#!/bin/sh
# Daniele Brugnara
# October, 2013
# Silently exec a command line passed as argument
[ ! $1 ] && echo "Please, don't joke me..." && exit 1
cmdLine="$1>/dev/null"
# if passed a second parameter, errors will be hidden
[ $2 ] && cmdLine+=" 2>/dev/null"
# not working
echo "Running \"$cmdLine\""
bash -c "$cmdLine" &
wait

Error with Bash script and ssh

I have a bash script where I ssh to a remote host and then create a file depending on the operating system (case statement in bash). When I execute this code on OS X, I expect the value Darwin to be evaluated and the file eg2.txt to be created. However, for some reason the evaluation fails to choose Darwin and it selects * and then creates the file none.txt. Has anyone run into a similar issue? Can someone tell what is wrong?
#!/bin/bash
ssh -l user $1 "cd Desktop;
opname=`uname -s`;
echo \"first\" > first.txt
case \"$opname\" in
"Darwin") echo \"Darwin\" > eg2.txt ;;
"Linux") sed -i \"/$2/d\" choice_list.txt ;;
*) touch none.txt ;;
esac"
P.S. I am running this code primarily on a Mac.
The problem is that your $opname variable is being expanded (into the empty string) by the Bash instance that's running ssh (i.e., on the client-side), rather than being passed over SSH to be handled by the Bash instance on the server-side.
To fix this, you can either use single-quotes instead of double-quotes:
#!/bin/bash
ssh -l user $1 'cd Desktop;
opname=`uname -s`;
echo "first" > first.txt
case "$opname" in
Darwin) echo "Darwin" > eg2.txt ;;
Linux) sed -i "/$2/d" choice_list.txt ;;
*) touch none.txt ;;
esac'
or else you can quote your $ using \:
#!/bin/bash
ssh -l user $1 "cd Desktop;
opname=`uname -s`;
echo \"first\" > first.txt
case \"\$opname\" in
"Darwin") echo \"Darwin\" > eg2.txt ;;
"Linux") sed -i \"/\$2/d\" choice_list.txt ;;
*) touch none.txt ;;
esac"

Problem with pidof in Bash script

I've written a script for me to start and stop my Perforce server. To shutdown the server I use the kill -SIGTERM command with the PID of the server daemon. It works as it should but there are some discrepancies in my script concerning the output behavior.
The script looks as follows:
#!/bin/sh -e
export P4JOURNAL=/var/log/perforce/journal
export P4LOG=/var/log/perforce/p4err
export P4ROOT=/var/local/perforce_depot
export P4PORT=1666
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
. /lib/lsb/init-functions
p4start="p4d -d"
p4stop="p4 admin stop"
p4user=perforce
case "$1" in
start)
log_action_begin_msg "Starting Perforce Server"
daemon -u $p4user -- $p4start;
echo "\n"
;;
stop)
echo "BLABLA"
echo "$(pidof /usr/local/bin/p4d)"
#daemon -u $p4user -- $p4stop;
p4dPid="$(pidof /usr/local/bin/p4d)"
echo $p4dPid
if [ -z "$(pidof /usr/local/bin/p4d)" ]; then
echo "ERROR: No Perforce Server running!"
else
echo "SUCCESS: Found Perforce Server running!\n\t"
echo "Shutting down Perforce Server..."
kill -15 $p4dPid;
fi
echo "\n"
;;
restart)
stop
start
;;
*)
echo "Usage: /etc/init.d/perforce (start|stop|restart)"
exit 1
;;
esac
exit 0
When p4d is running the stop block works as intended, but when there is no p4d running the script with stop only outputs BLABLA and an empty new line because of the echo "$(pidof /usr/local/bin/p4d)". The error message stating that no server is running is never printed. What am I doing wrong here?
PS: The part if [ -z "$(pidof /usr/local/bin/p4d)" ]; then has been changed from if [ -z "$p4dPid" ]; then for debug reasons.
EDIT: I narrowed down the problem. If I don't use the p4dPid variable and comment out the lines p4dPid="$(pidof /usr/local/bin/p4d)" and echo $p4dPid the if block is processed and the error messages is printed. Still I don't unterstand what is causing this behavior.
EDIT 2: Problem solved!
The -e in #!/bin/sh -e was causing the shell to exit the script after any statement returning a non-zero return value.
When your service is not running, the command
echo "$(pidof /usr/local/bin/p4d)"
is processed as
echo ""
because pidof did not return any string. So the command outputs an empty line.
If you do not want this empty line, then just remove this statement, after all you print an error message when the process is not running.
Problem solved!
The -e in #!/bin/sh -e was causing the shell to exit after any statement returning a non-zero return value.

How to check in a bash script if something is running and exit if it is

I have a script that runs every 15 minutes but sometimes if the box is busy it hangs and the next process will start before the first one is finished creating a snowball effect. How can I add a couple lines to the bash script to check to see if something is running first before starting?
You can use pidof -x if you know the process name, or kill -0 if you know the PID.
Example:
if pidof -x vim > /dev/null
then
echo "Vim already running"
exit 1
fi
Why don't set a lock file ?
Something like
yourapp.lock
Just remove it when you process is finished, and check for it before to launch it.
It could be done using
if [ -f yourapp.lock ]; then
echo "The process is already launched, please wait..."
fi
In lieu of pidfiles, as long as your script has a uniquely identifiable name you can do something like this:
#!/bin/bash
COMMAND=$0
# exit if I am already running
RUNNING=`ps --no-headers -C${COMMAND} | wc -l`
if [ ${RUNNING} -gt 1 ]; then
echo "Previous ${COMMAND} is still running."
exit 1
fi
... rest of script ...
pgrep -f yourscript >/dev/null && exit
This is how I do it in one of my cron jobs
lockfile=~/myproc.lock
minutes=60
if [ -f "$lockfile" ]
then
filestr=`find $lockfile -mmin +$minutes -print`
if [ "$filestr" = "" ]; then
echo "Lockfile is not older than $minutes minutes! Another $0 running. Exiting ..."
exit 1
else
echo "Lockfile is older than $minutes minutes, ignoring it!"
rm $lockfile
fi
fi
echo "Creating lockfile $lockfile"
touch $lockfile
and delete the lock file at the end of the script
echo "Removing lock $lockfile ..."
rm $lockfile
For a method that does not suffer from parsing bugs and race conditions, check out:
BashFAQ/045 - How can I ensure that only one instance of a script is running at a time (mutual exclusion)?
I had recently the same question and found from above that kill -0 is best for my case:
echo "Starting process..."
run-process > $OUTPUT &
pid=$!
echo "Process started pid=$pid"
while true; do
kill -0 $pid 2> /dev/null || { echo "Process exit detected"; break; }
sleep 1
done
echo "Done."
To expand on what #bgy says, the safe atomic way to create a lock file if it doesn't exist yet, and fail if it doesn't, is to create a temp file, then hard link it to the standard lock file. This protects against another process creating the file in between you testing for it and you creating it.
Here is the lock file code from my hourly backup script:
echo $$ > /tmp/lock.$$
if ! ln /tmp/lock.$$ /tmp/lock ; then
echo "previous backup in process"
rm /tmp/lock.$$
exit
fi
Don't forget to delete both the lock file and the temp file when you're done, even if you exit early through an error.
Use this script:
FILE="/tmp/my_file"
if [ -f "$FILE" ]; then
echo "Still running"
exit
fi
trap EXIT "rm -f $FILE"
touch $FILE
...script here...
This script will create a file and remove it on exit.

Resources