3

Following from this question: How to get the pid of the last executed command in shell script?

The thread there is good and clear, but what if the command actually doesn't require a '&' at the end in order to background itself? Is it then possible to get it's PID ?

Nota bene: I do NOT want to use ps and grep; I am asking because theoretically, just after running a command, the PID of that process supposed to be find-able.

An example of an app that backgrounds itself -- this is not the one I need, I need a generic solution for things like any stuff which go background by itself (some forcing it with no "foreground" option at all) -- but for testing a solution this will work just fine:

my-app script:

#!/bin/bash

(
sleep 10
echo test
sleep 5
) &

as you can see the '&' is inside the script and not outside, so, starting it won't require &:

./my-app ; echo $! | od -c ; jobs | od -c
0000000  \n
0000001
0000000

$! is null, and jobs returns nothing. Still, after 10 seconds, a "test" message will appear; also:

ps x | grep my-app | grep -v grep        
14986 pts/5    S      0:00 /bin/bash ./my-app

Update, after some comments saying that bash jobs don't apply here; I'm hoping for a solution and my hope is not limited to bash :)

user40404
  • 131
  • 2
    Could you [edit] your question and give n example of a command that goes to the background by itself? That would make it easier to test and answer you. – terdon Oct 21 '15 at 13:20
  • If the command backgrounds itself, then bash is not engaged in any jobs management. Thus, there's no mechanism in bash to hand you the PID. – glenn jackman Oct 21 '15 at 13:49
  • you get the pid from the process that backgrounds itself because it lets you know. else you dont because it doesnt let you know. – mikeserv Oct 21 '15 at 14:15
  • Another way of saying what mikeserv said -- in the general case, there is no way to tell. For specific applications, they may drop a PID file somewhere for its own future reference. – Jeff Schaller Oct 21 '15 at 15:27

3 Answers3

2

To background itself, that application forks a child process and exits the parent. The shell knows of the parent process as that's the one it has forked itself before executing the command itself, but it has no visibility on the children or grand-children that that process may have spawned.

A simpler example of such a backgrounding command would be:

$ sh -c 'ps -j; sleep 10 &'
  PID  PGID   SID TTY          TIME CMD
 6562  6562 14469 pts/13   00:00:00 sh
 6563  6562 14469 pts/13   00:00:00 ps
14469 14469 14469 pts/13   00:00:00 zsh

My zsh knows about that sh process and its process group.

However, all it sees is that 6562 process exiting. It has no way to know that 6562 process has spawned a 6563 one.

$ ps -j
  PID  PGID   SID TTY          TIME CMD
 6564  6562 14469 pts/13   00:00:00 sleep
 6565  6565 14469 pts/13   00:00:00 ps
14469 14469 14469 pts/13   00:00:00 zsh

However, you can see the process running sleep is also in that 6562 process group (though, there's nothing stopping commands start new process groups, or even sessions (as daemons typically do)).

Those process groups are only created when the shell is interactive though.

Another thing you could do is:

cmd | cat &
wait

If the processes that cmd spawns don't close their stdout, then cat won't die until they have all died.

2

This is not possible in general. It may be possible in your specific case.

Just after running a background command, the process ID is discoverable from the parent. If you run a foreground command (a child of the master program), and that command in turn runs a background command (a grandchild of the master program), the master program has no direct visibility on its grandchild: all it knows is that it's run a child process, which has now exited.

The kernel keeps track of child→parent process relationships. You can run ps -o ppid= -p $pid to see the process ID of the parent process of the process whose ID is $pid. The kernel does not keep track of a process's grandparent. Furthermore, if the parent dies, the process is adopted by init (process number 1), so its parent process ID will be 1 from that point on.

There are several other process attributes that are inherited and that you might try to keep track of. However the grandchild can divorce from any of these attributes. If the grandchild is intended to run as a daemon, it probably will attempt to isolate itself as much as possible (either from the intermediate child or from the grandchild itself), so that it isn't tied to your interactive session and doesn't risk getting caught in anything happening to this session.

You can look for processes in the same process group as the current process (ps -o pgid=)). This will catch any other process started within the same process group, but conversely miss the grandchild if it runs in its own process group due to a call to setpgid or setpgrp in the child or the grandchild, which daemons do.

You can look for processes in the same session ID as the current process (ps -o sid=)). This will catch any other process started within the same session ID, but conversely miss the grandchild if it runs in its own session due to a call to setsid in the child or the grandchild, which daemons do.

You can open a temporary file and look for processes that have this file open (`fuser "$tmpfile"). This is more reliable because it will only catch processes started from the part of the master process that had this file open, it won't catch whatever other components of the master process might have done. However like other solutions it will miss the grandchild if it or the intermediate child closed the file descriptors it didn't use, which daemons do.

Most daemons have a command line option to stay in the foreground. You can then run daemon --foreground & daemon_pid=$!, and it's up to you to take precautions to avoid the daemon being caught in the session exit (nohup daemon --foreground </dev/null >daemon.log 2>&1 & is a good start).

1

You can get some information by using strace on the script, but it does keep your strace running until the sub-commands finish. Eg,

$ strace -b execve -e trace=none -e signal=none -f my-app
Process 21697 attached             <-- is the () &
[pid 21696] +++ exited with 0 +++  <-- is my-app ending
Process 21698 attached             <-- is the sleep 10
Process 21698 detached
test
Process 21702 attached             <-- is the sleep 5
Process 21702 detached
+++ exited with 0 +++

so we have the pids of the sub-shell, and the commands run there, assuming echo is a shell builtin so it is not tracked. -b execve is to detach on exec, -f to follow children, the -e to reduce output by not tracing anything.

meuh
  • 51,383