Run current bash script in background - bash

Usually I add "&" character to start my process in backgroud, exemple :
user#pc:~$ my_script &
But how can I make it in background without "&" character ?
#!/bin/bash
#What can I add here to hide current process ($$) and to release focus ?
start_server()
{
#my script here with infinite loop ...
}
Thanks guys.

#!/bin/bash
if [[ "$1" != "--nodaemon" ]]; then
( "$0" --nodaemon "$#" </dev/null &>/dev/null & )
else
shift
fi
#...rest of script
What this does is check to see if its first argument is "--nodaemon", and if so fire itself ("$0") off in the background with the argument "--nodaemon", which'll prevent it from trying to re-background itself in a sort of infinite loop.
Note that putting this as the first thing in the script will make it always run itself in the background. If it only needs to drop into the background under certain conditions (e.g. when run with the argument "start"), you'd have to adjust this accordingly. Maybe something like this:
#!/bin/bash
start_server()
{
#my script here with infinite loop ...
}
if [[ "$1" = "start" ]]; then
( "$0" start-nodaemon </dev/null &>/dev/null & )
elif [[ "$1" = "start-nodaemon" ]]; then
start_server
elif #.....

Related

Two questions (sorry if i'm hoarding too much)

1) I'm creating a program in bash that asks for a filename, then runs convert for such a filename after assigning it a variable $name with read. The directory it is searching in also has spaces, which I have sorted out the following way:
read -p "please input filename: " file
convert /path/to/that/file/with/bothersome\ spaces/here/$file.png /destination/path/to/that/file/with\ some/spaces/$file.pdf
All is nice... until the filename itself happens to have spaces on it.
I have attempted putting in the variable $file typing it $FILE , $"file" , "$file" and '$file' but none seem to work.
2) Is there any way I can do a goto or loop to a certain line in the script within an if statement? just so I don't have to rewrite those operations again (which seems pointless).
I'm making something of this sort:
echo "do this or that? [a, b] "
read input
if [[ $input == "a" ]]; then
{
app1
app2
app3
} &> /dev/null
(around this point I'd like to have something that allowed me to ask again if I want to loop this process or quit the script altogether.) and then...
else
{
app4
app5
app6
} &> /dev/null
(same as the previous parenthesis)
fi
exit
So yeah, that would be it. If both can't be answered, I can manage just getting answer to the second and re-running that script as needed (in case i need to perform it a second time or whatever).
Thank you very much for your help.
Edit: here is the original file
#!/bin/bash
echo "Do this type of file or the other? [a, b] "
read input
if [[ $input == "A" || $input == "a" ]]; then
{
/usr/bin/app1 "/path/to/open" &
/usr/bin/app2 "/file/to/open.file" &
/usr/bin/app3 "/file/to/open.file" &
/usr/bin/app4 &
/usr/bin/app5 &
} &> /dev/null
while :; do #attempting what Barmar said. this is the particular part i want to loop from each section
echo "stuff opened"
read -p "please input filename: " file
convert /original/file/"$file".png /converted/file/"$file".pdf
echo "do another one? [Y,N] " stop
if [[ $stop == "n" || $stop == "N" ]]; then
break
wait
else
{
/usr/bin/app6 "/different/file/to/open" &
/usr/bin/app7 "/different/file/to/open.file" &
/usr/bin/app8 "/different/file/to/open.file" &
/usr/bin/app9 &
/usr/bin/app10 &
} &> /dev/null
while :; do #attempting what Barmar said. this is the particular part i want to loop from each section
read -p "please input file: " file
convert /original/file/"$file".png /converted/file/"$file".pdf
echo "do another one? [Y,N] " stop
if [[ $stop == "n" || $stop == "N" ]]; then
break
wait
fi
doing it in this particular way it breaks the if statements.
What I want in essence is, that the first IF either takes the first block of apps, then the convert part, OR the second block of apps, then its convert part. And then another IF inside of both convert parts to loop either of them as needed.
1) Double quotes should do it.
convert /path/to/that/file/with/bothersome\ spaces/here/"$file".png /destination/path/to/that/file/with\ some/spaces/"$file".pdf
2) Use a while loop, and use break to stop the loop.
while :; do
echo "do this or that? [a, b] "
read input
if [[ $input == "a" ]]; then
{
app1
app2
app3
} &> /dev/null
read -p "Stop? " stop
if [[ $stop == "y" ]]; then
break
fi
else
{
app4
app5
app6
} &> /dev/null
fi
done

Bash pass argument for array selection

I'm trying to pass the second argument to get an array and loop trough but im getting this error: ${$2[#]}: bad substitution
my code is:
/etc/init.d/displaycameras start c1
#!/bin/bash
dis1cam1="screen -dmS dis1cam1 sh -c 'omxplayer --avdict rtsp_transport:tcp --win \"0 0 640 428\" rtsp://myvideo --live -n -1'";
camera_feeds=('c1=(dis1cam1 dis1cam2 dis1cam3 dis1cam4 dis1cam5 dis1cam6 dis1cam8 dis1cam9)' 'c2=(dis2cam1 dis2cam2 dis2cam3 dis2cam4)')
for elt in "${camera_feeds[#]}";do eval $elt;done
# Start displaying camera feeds
case "$1" in
start)
for i in "${$2[#]}"
do
eval eval '$'$i
done
echo "Camera Display 1 Started"
;;
Is there a way to pass the 2nd argument to call the c2 set ?
in this way is working perfect:
#!/bin/bash
dis1cam1="screen -dmS dis1cam1 sh -c 'omxplayer --avdict rtsp_transport:tcp --win \"0 0 640 428\" rtsp://myvideo --live -n -1'";
camera_feeds=('c1=(dis1cam1 dis1cam2 dis1cam3 dis1cam4 dis1cam5 dis1cam6 dis1cam8 dis1cam9)' 'c2=(dis2cam1 dis2cam2 dis2cam3 dis2cam4)')
for elt in "${camera_feeds[#]}";do eval $elt;done
# Start displaying camera feeds
case "$1" in
start)
for i in "${c1[#]}"
do
eval eval '$'$i
done
echo "Camera Display 1 Started"
;;
I would strongly advise implementing this differently.
#!/usr/bin/env bash
die() { echo "$*" >&2; exit 1; }
[[ $BASH_VERSION = [0-3]* ]] && die "Bash 4.3 or newer needed"
[[ $BASH_VERSION = 4.[0-2].* ]] && die "Bash 4.3 or newer needed"
dis1cam1() { : "code to start camera dis1cam1 here"; )
dis1cam2() { : "code to start camera dis1cam2 here"; )
# ...etc...
camera_feeds__c1=(dis1cam1 dis1cam2 dis1cam3 dis1cam4 dis1cam5 dis1cam6 dis1cam8 dis1cam9)
camera_feeds__c2=(dis2cam1 dis2cam2 dis2cam3 dis2cam4)
# here, we're showing the iterate-over-all-feeds case
# you can just set var=camera_feeds__c1 yourself if you prefer
for var in "${!camera_feeds__#}"; do # var will be camera_feeds__c1 or camera_feeds__c2
feed_name=${var#camera_feeds__} # feed_name will be "c1" or "c2"
declare -n camera_feeds=$var
for i in "${camera_feeds[#]}"; do
echo "Starting $i in feed $feed_name" >&2
"$i" # look up and run code in variable named in $i
done
unset -n camera_feeds
done
"${camera_feeds__#}" expands to the list of shell variables whose names start with camera_feeds__; this is thus the name of our two arrays.
declare -n camera_feeds=$var then makes camera_feeds an alias for the array presently being iterated over, such that for i in "${camera_feeds[#]}" iterates over that array.
unset -n camera_feeds clears this association.

Explain some tips of bash

I get a piece of code for PID file control.
The style of programmers, I don't understand..
I don't know -->
Use of && on
[[ $mypid -ne $procpid ]] **&&**
And relaunch ourselves properly (does not work on MacosX)
$0 $# &
Code complete...
function createpidfile() {
mypid=$1
pidfile=$2
#Close stderr, don't overwrite existing file, shove my pid in the lock file.
$(exec 2>&-; set -o noclobber; echo "$mypid" > "$pidfile")
[[ ! -f "$pidfile" ]] && exit #Lock file creation failed
procpid=$(<"$pidfile")
[[ $mypid -ne $procpid ]] && {
#I'm not the pid in the lock file
# Is the process pid in the lockfile still running?
isrunning "$pidfile" || {
# No. Kill the pidfile and relaunch ourselves properly.
rm "$pidfile"
$0 $# &
}
exit
}
}
I'm lost
[[ ! -f "$pidfile" ]] && exit means "if there is no file called $pidfile then exit" (using the short-circuit evaluation) - exit will not be evaluated if the file exists.
$0 $# &:
$0 - the first argument in the command line (meaning the executable itself);
$# - all the remaining arguments passed onto the command line;
& - send the process to background after the launch.
command1 && command2
command2 is executed if, and only if, command1 returns an exit status of zero.
$0 is the name of the actual binary.
$# are all parameters.
and the closing & sends the process to the background.
Everything is documented in the bash manual See e.g. section 3.4.2 Special Parameters
&& is a logical AND.
If the condition [[ $mypid -ne $procpid ]] is true, the code in the block {...} gets executed.
$0 $# & restarts the script in the background (with the same arguments).
$0 is the command that invoked the script
$# is the list of all arguments passed to the script
& indicates the previous command should be executed in the background
It's boolean short-circuiting - if the bit before the && (and) operator evaluates to be false then there's no need to execute the second part (the block between { and }. The same trick is used with the || operator, which will only execute the second block if the first block returned false.

writing from a function in a Bash script leaking file descriptors

We have a shell script that is called by cron and runs as root.
This script outputs logging and debug info, and has been failing at one certain point. This point varies based on how much output the script creates (it fails sooner if we enable more debugging output, for example).
However, if the script is called directly, as a user, then it works without a problem.
We have since created a simplified test case which demonstrates the problem.
The script is:
#!/bin/bash
function log_so () {
local msg="$1"
if [ -z "${LOG_FILE}" ] ; then warn_so "It's pointless use log_so() if LOG_FILE variable is undefined!" ; return 1 ; fi
echo -e "${msg}"
echo -e "${msg}" >> ${LOG_FILE}
(
/bin/true
)
}
LOG_FILE="/usr/local/bin/log_bla"
linenum=1
while [[ $linenum -lt 2000 ]] ; do
log_so "short text: $linenum"
let linenum++
done
The highest this has reached is 244 before dying (when called via cron).
Some other searches recommended using a no-op subshell from the function and also calling /bin/true but not only did this not work, the subshell option is not feasible in the main script.
We have also tried changing the file descriptor limit for root, but that did not help, and have tried using both #!/bin/sh and #!/bin/bash for the script.
We are using bash 4.1.5(1)-release on Ubuntu 10.04 LTS.
Any ideas or recommendations for a workaround would be appreciated.
What about opening a fd by hand and cleaning it up afterwards? I don't have a bash 4.1 to test with, but it might help.
LOG_FILE="/usr/local/bin/log_bla"
exec 9<> "$LOG_FILE"
function log_so () {
local msg="$1"
if [ -z "${LOG_FILE}" ] ; then warn_so "It's pointless use log_so() if LOG_FILE variable is undefined!" ; return 1 ; fi
echo -e "${msg}"
echo -e "${msg}" >&9
return 0
}
linenum=1
while [[ $linenum -lt 2000 ]] ; do
log_so "short text: $linenum"
let linenum++
done
exec 9>&-

How to terminate script's process tree in Cygwin bash from bash script

I have a Cygwin bash script that I need to watch and terminate under certain conditions - specifically, after a certain file has been created. I'm having difficulty figuring out how exactly to terminate the script with the same level of completeness that Ctrl+C does, however.
Here's a simple script (called test1) that does little more than wait around to be terminated.
#!/bin/bash
test -f kill_me && rm kill_me
touch kill_me
tail -f kill_me
If this script is run in the foreground, Ctrl+C will terminate both the tail and the script itself. If the script is run in the background, a kill %1 (assuming it is job 1) will also terminate both tail and the script.
However, when I try to do the same thing from a script, I'm finding that only the bash process running the script is terminated, while tail hangs around disconnected from its parent. Here's one way I tried (test2):
#!/bin/bash
test -f kill_me && rm kill_me
(
touch kill_me
tail -f kill_me
) &
while true; do
sleep 1
test -f kill_me && {
kill %1
exit
}
done
If this is run, the bash subshell running in the background is terminated OK, but tail still hangs around.
If I use an explicitly separate script, like this, it still doesn't work (test3):
#!/bin/bash
test -f kill_me && rm kill_me
# assuming test1 above is included in the same directory
./test1 &
while true; do
sleep 1
test -f kill_me && {
kill %1
exit
}
done
tail is still hanging around after this script is run.
In my actual case, the process creating files is not particularly instrumentable, so I can't get it to terminate of its own accord; by finding out when it has created a particular file, however, I can at that point know that it's OK to terminate it. Unfortunately, I can't use a simple killall or equivalent, as there may be multiple instances running, and I only want to kill the specific instance.
/bin/kill (the program, not the bash builtin) interprets a negative PID as “kill the process group” which will get all the children too.
Changing
kill %1
to
/bin/kill -- -$$
works for me.
Adam's link put me in a direction that will solve the problem, albeit not without some minor caveats.
The script doesn't work unmodified under Cygwin, so I rewrote it, and with a couple more options. Here's my version:
#!/bin/bash
function usage
{
echo "usage: $(basename $0) [-c] [-<sigspec>] <pid>..."
echo "Recursively kill the process tree(s) rooted by <pid>."
echo "Options:"
echo " -c Only kill children; don't kill root"
echo " <sigspec> Arbitrary argument to pass to kill, expected to be signal specification"
exit 1
}
kill_parent=1
sig_spec=-9
function do_kill # <pid>...
{
kill "$sig_spec" "$#"
}
function kill_children # pid
{
local target=$1
local pid=
local ppid=
local i
# Returns alternating ids: first is pid, second is parent
for i in $(ps -f | tail +2 | cut -b 10-24); do
if [ ! -n "$pid" ]; then
# first in pair
pid=$i
else
# second in pair
ppid=$i
(( ppid == target && pid != $$ )) && {
kill_children $pid
do_kill $pid
}
# reset pid for next pair
pid=
fi
done
}
test -n "$1" || usage
while [ -n "$1" ]; do
case "$1" in
-c)
kill_parent=0
;;
-*)
sig_spec="$1"
;;
*)
kill_children $1
(( kill_parent )) && do_kill $1
;;
esac
shift
done
The only real downside is the somewhat ugly message that bash prints out when it receives a fatal signal, namely "Terminated", "Killed" or "Interrupted" (depending on what you send). However, I can live with that in batch scripts.
This script looks like it'll do the job:
#!/bin/bash
# Author: Sunil Alankar
##
# recursive kill. kills the process tree down from the specified pid
#
# foreach child of pid, recursive call dokill
dokill() {
local pid=$1
local itsparent=""
local aprocess=""
local x=""
# next line is a single line
for x in `/bin/ps -f | sed -e '/UID/d;s/[a-zA-Z0-9_-]\{1,\}
\{1,\}\([0-9]\{1,\}\) \{1,\}\([0-9]\{1,\}\) .*/\1 \2/g'`
do
if [ "$aprocess" = "" ]; then
aprocess=$x
itsparent=""
continue
else
itsparent=$x
if [ "$itsparent" = "$pid" ]; then
dokill $aprocess
fi
aprocess=""
fi
done
echo "killing $1"
kill -9 $1 > /dev/null 2>&1
}
case $# in
1) PID=$1
;;
*) echo "usage: rekill <top pid to kill>";
exit 1;
;;
esac
dokill $PID

Resources