13

In *nix world, is there a way for shell script to have information about which program has executed it?


Example:

/path/to/script1 /path/to/script_xyz

in this imaginary scenario, script_xyz would have path information (/path/to/script1)

or

process PID

of entity that have executed it.

Note: I'm curious about different solutions and approaches, I don't expect exactly this to be actually possible

  • 3
    Your subject asks which program executed the script. But your actual question seems to be asking for the interpreter of the script. Which of the two is your question really about? – kasperd Jul 23 '16 at 18:01
  • @kasperd You are right. Question was about program, but that's actually interpreter. That's why I had feeling that this is not possible in the first place. – Miloš Đakonović Jul 24 '16 at 8:21
23

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".

8

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.

  • Thanks. Running your example scripts I get s1, s2 and PPID values but then, in multiple lines after ERROR: Unsupported SysV option. and several lines with additional explanation and - empty value for Parent command – Miloš Đakonović Jul 23 '16 at 9:49
  • John's using some ps feature that's not available (or is provided differently) on your platform, check your ps(1) manual page. – Jasen Jul 23 '16 at 10:11
  • @Miloshio I tested the above using ps from the procps-ng package, version 3.3.12. As Jasen suggested, you are likely using a different version which may require a different syntax for printing the parent's command line. Try ps -f | grep $PPID. – John1024 Jul 23 '16 at 19:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.