views:

48

answers:

3

Hi All,

I have written a script that relies on other server responses (uses wget to pull data), and I want it to always be run in the background unquestionably. I know one solution is to just write a wrapper script that will call my script with an '&' appended, but I want to avoid that clutter.

Is there a way for a bash (or zsh) script to determine if it was called with say ./foo.sh&, and if not, exit and re-launch itself as such?

Thanks all!

+1  A: 

I do not know, how to do this, but you may set variable in parent script and check for it in child:

if [[ -z "$_BACKGROUNDED" ]] ; then
    _BACKGROUNDED=1 exec $0 $@ & exit
fi
# Put code here

Works both in bash and zsh.

ZyX
I'm going to accept this answer, it seems to do what I want, except in your solution "endif" should be "fi" ;-) Thanks very much.
jparanich
Extra thought: I realize a fussy person could argue this snipplet will re-launch the script even if backgrounded at invocation, but the extra complexity of checking this isn't required for me.
jparanich
A: 

I don't write shell scripts a long time ago, but I can give you a very good idea (I hope). You can check the value of $$ (this is the PID of the process) and compare with the output of the command "jobs -l". This last command will return the PID of all the backgrounded processes (jobs) and if the value of $$ is contained in the result of the "jobs -l", this means that the current script is running on background.

carlson
ZyX
carlson
@carlson How do you run your script? I do not see this behavior neither in bash nor in zsh.
ZyX
+3  A: 

The definition of a background process (I think) is that it has a controlling terminal but it is not part of that terminal's foreground process group. I don't think any shell, even zsh, gives you any access to that information through a builtin.

On Linux (and perhaps other unices), the STAT column of ps includes a + when the process is part of its terminal's foreground process group. So a literal answer to your question is that you could put your script's content in a main function and invoke it with:

case $(ps -o stat= -p $$) in
  *+*) main "$@" &;;
  *) main "$@";;
esac

But you might as well run main "$@" & anyway. On Unix, fork is cheap.

However, I strongly advise against doing what you propose. This makes it impossible for someone to run your script and do something else afterwards — one would expect to be able to write your_script; my_postprocessing or your_script && my_postprocessing, but forking the script's main task makes this impossible. Considering that the gain is occasionally saving one character when the script is invoked, it's not worth making your script markedly less useful in this way.

If you really mean for the script to run in the background so that the user can close his terminal, you'll need to do more work — you'll need to daemonize the script, which includes not just backgrounding but also closing all file descriptors that have the terminal open, making the process a session leader and more. I think that will require splitting your script into a daemonizing wrapper script and a main script. But daemonizing is normally done for programs that never terminate unless explicitly stopped, which is not the behavior you describe.

Gilles
Hi Gilles, thanks for the detailed response. The solution you provide works along with ZyX's, but I accepted his as it is more self-contained and doesn't rely on ps or any branching. For the record, the script is solely for myself, will never be touched by another user and is in /etc/init.d so it'll not have terminal closure. Thanks =) +1
jparanich