53

Let's assume I have 3 shell scripts:

script_1.sh

#!/bin/bash
./script_3.sh

script_2.sh

#!/bin/bash
./script_3.sh

the problem is that in script_3.sh I want to know the name of the caller script.

so that I can respond differently to each caller I support

please don't assume I'm asking about $0 cause $0 will echo script_3 every time no matter who is the caller

here is an example input with expected output

  • ./script_1.sh should echo script_1

  • ./script_2.sh should echo script_2

  • ./script_3.sh should echo user_name or root or anything to distinguish between the 3 cases?

Is that possible? and if possible, how can it be done?

this is going to be added to a rm modified script... so when I call rm it do something and when git or any other CLI tool use rm it is not affected by the modification

codeforester
  • 34,080
  • 14
  • 96
  • 122
a14m
  • 7,248
  • 7
  • 52
  • 65
  • 11
    Beware modifying the `rm` command (by giving using cover scripts called `rm`, or aliases, or functions). You'll come to rely on the functionality, and then one day you'll find yourself using the raw `rm` command without the protections, and you'll do serious damage because you've been lulled into a false sense of security. – Jonathan Leffler Dec 13 '13 at 18:27

9 Answers9

77

Based on @user3100381's answer, here's a much simpler command to get the same thing which I believe should be fairly portable:

PARENT_COMMAND=$(ps -o comm= $PPID)

Replace comm= with args= to get the full command line (command + arguments). The = alone is used to suppress the headers.

See: http://pubs.opengroup.org/onlinepubs/009604499/utilities/ps.html

Thomas Guyot-Sionnest
  • 1,877
  • 19
  • 15
21

In case you are sourceing instead of calling/executing the script there is no new process forked and thus the solutions with ps won't work reliably.

Use bash built-in caller in that case.

$ cat h.sh 
#! /bin/bash 
function warn_me() { 
    echo "$@" 
    caller 
} 
$ cat g.sh 
#!/bin/bash 
source h.sh 
warn_me "Error: You didn't do something" 
$ . g.sh 
Error: You didn't do something 3 
g.sh
$

Source

Kashyap
  • 13,836
  • 12
  • 58
  • 96
20

The $PPID variable holds the parent process ID. So you could parse the output from ps to get the command.

#!/bin/bash
PARENT_COMMAND=$(ps $PPID | tail -n 1 | awk "{print \$5}")
user3100381
  • 454
  • 2
  • 5
  • Inventive, and probably about as good as you're going to get. Not particularly nice, though. – Jonathan Leffler Dec 13 '13 at 18:26
  • On some platforms you just need the right options to `ps`, so the tail and Awk (which could easily be refactored to just Awk, btw) can be avoided. – tripleee Dec 13 '13 at 18:32
  • 3
    this prints `/bin/bash` – a14m Dec 13 '13 at 18:33
  • 2
    @artmees Try it with `awk "{print \$6}"` – Reinstate Monica Please Dec 13 '13 at 19:03
  • using `$6` make `./script_3` prints `'\n'` empty string and `./script_2` return `'\n./script_3'` – a14m Dec 13 '13 at 19:31
  • instead could you explain what this `ps $PPID | tail -n 1 | awk "{print \$6}"` should output and i might figure it out – a14m Dec 13 '13 at 19:32
  • @artmees I think I was looking at a modified ps, $5 is correct. You can just split the commands up to see what they do. First execute `ps $PPID`, then `ps $PPID | tail -n 1` , then add the awk. You could also add the F flag to`ps`to get command arguments, though sed of the file ps reads still feels slightly simpler. – Reinstate Monica Please Dec 13 '13 at 19:48
  • Found another site that was recommending ps -ocommand= -p $PPID, which then removes the need for a tail command. – Iain Hallam Apr 28 '14 at 16:09
4

Based on @J.L.answer, with more in depth explanations (the only one command that works for me ()) :

cat /proc/$PPID/comm

gives you the name of the command of the parent

If you prefer the command with all options, then :

cat /proc/$PPID/cmdline

explanations :

  • $PPID is defined by the shell, it's the of the parent processes
  • in /proc/, you have some dirs with the of each process (). Then, if you cat /proc/$PPID/comm, you echo the command name of the PID

Check man proc

Gilles Quenot
  • 154,891
  • 35
  • 213
  • 206
2

Couple of useful files things kept in /proc/$PPID here

  • /proc/*some_process_id*/exe A symlink to the last executed command under *some_process_id*
  • /proc/*some_process_id*/cmdline A file containing the last executed command under *some_process_id* and null-byte separated arguments

So a slight simplification.

sed 's/\x0/ /g' "/proc/$PPID/cmdline"
Reinstate Monica Please
  • 10,557
  • 3
  • 26
  • 45
1

If you have /proc:

$(cat /proc/$PPID/comm)
Gilles Quenot
  • 154,891
  • 35
  • 213
  • 206
J.L.
  • 19
  • 1
0

You can simply use the command below to avoid calling cut/awk/sed:

ps --no-headers -o command $PPID

If you only want the parent and none of the subsequent processes, you can use:

ps --no-headers -o command $PPID | cut -d' ' -f1
HoldOffHunger
  • 15,349
  • 8
  • 79
  • 115
Six
  • 4,562
  • 3
  • 27
  • 37
0

Declare this:

PARENT_NAME=`ps -ocomm --no-header $PPID`

Thus you'll get a nice variable $PARENT_NAME that holds the parent's name.

-3

You could pass in a variable to script_3.sh to determine how to respond...

script_1.sh

#!/bin/bash
./script_3.sh script1

script_2.sh

#!/bin/bash
./script_3.sh script2

script_3.sh

#!/bin/bash
if [ $1 == 'script1' ] ; then
  echo "we were called from script1!"
elsif [ $1 == 'script2' ] ; then
  echo "we were called from script2!"
fi
Donovan
  • 15,789
  • 4
  • 21
  • 34
  • this is not possible as i'm trying add a script that handles special case of `rm` and i want it to be called if i called `rm` not if `git` used `rm` to remove some info. – a14m Dec 13 '13 at 18:00
  • please clarify your original question then, because that was not clear from your examples. – Donovan Dec 13 '13 at 18:01
  • I do not think it is possible to obtain the calling program from within a bash script the way you want, I'm sorry. – Donovan Dec 13 '13 at 18:02