Is there a way for shell script to know which program has executed it?
There's often confusion between process forking and execution.
When you do at the prompt of a bash
shell.
$ sh -c 'exec env ps'
The process P1 issuing that $
prompt is currently running bash
code. That bash
code forks a new process P2 that executes /bin/sh
which then executes /usr/bin/env
, which then executes /bin/ps
.
So P2 has in turn executed code of bash
, sh
, env
and ps
.
ps
(or any other command like a script we would use instead here) has no way to know that it has been executed by the env
command.
All it can do is find out what its parent process id is, which in this case would be either P1 or 1
if P1 has died in the interval or on Linux another process that has been designated as a subreaper instead of 1
.
It can then query the system for what command that process is currently running (like with readlink /proc/<pid>/exe
on Linux) or what arguments where passed to the last command it executed (like with ps -o args= -p <pid>
).
If you want your script to know what invoked it, a reliable way would be to have the invoker tell it. That could be done for instance via an environment variable. For instance script1
could be written as:
#! /bin/sh -
INVOKER=$0 script2 &
And script2
:
#! /bin/sh -
printf '%s\n' "I was invoked by $INVOKER"
# and in this case, we'll probably find the parent process is 1
# (if not now, at least one second later) as script1 exited just after
# invoking script2:
ps -fp "$$"
sleep 1
ps -fp "$$"
exit
$INVOKER
will (generally) contain a path to script1
. In some cases, it may be a relative path though, and the path will be relative to the current working directory at the time script1
started. So if script1
changes the current working directory before calling script2
, script2
will get wrong information as to what called it. So it may be preferable to make sure $INVOKER
contains an absolute path (preferably keeping the basename) like by writing script1
as:
#! /bin/sh -
mypath=$(
mydir=$(dirname -- "$0") &&
cd -P -- "$mydir" &&
pwd -P) && mypath=$mypath/$(basename -- "$0") || mypath=$0
... some code possibly changing the current working directory
INVOKER=$mypath script2
In POSIX shells, $PPID
will contain the pid of the parent of the process that executed the shell at the time of that shell initialisation. After that, as seen above, the parent process may change if the process of id $PPID
dies.
zsh
in the zsh/system
module, can query the current parent pid of the current (sub-)shell with $sysparams[ppid]
. In POSIX shells, you can get the current ppid of the process that executed the interpreter (assuming it's still running) with ps -o ppid= -p "$$"
. With bash
, you can get the ppid of the current (sub-)shell with ps -o ppid= -p "$BASHPID"
.
Yes, a program can know who its parent is.
To illustrate, let's create two bash scripts. The first one reports its PID and starts the second script:
$ cat s1.sh
#!/bin/bash
echo s1=$$
bash s2.sh
The second script reports its process ID, the PID of its parent, and the command line used to run the parent:
$ cat s2.sh
#!/bin/bash
echo s2=$$ PPID=$PPID
echo "Parent command: $(ps -o cmd= -q $PPID)"
Now, let's run it:
$ bash s1.sh
s1=17955
s2=17956 PPID=17955
Parent command: bash s1.sh
As you can see the second script does, in fact, know the PID of its parent. Using ps
, that PID reveals the command line used to invoke the parent.
For a discussion of PPID in more depth, see Stéphane Chazelas's answer.