red_draign has asked for the wisdom of the Perl Monks concerning the following question:

hola to those at the monastary! i have a perl script, which is called by either a shell script or a bat file. i need to know the name of this calling shell/bat from within the called perl script. so, the obvious answer is "caller", but every variation i've tried does not get me the name of the shell/bat -- the highest up i can get is the name of the perl script. short of passing the name of the shell/bat to the perl script as an argument, is there anything that would yield the desired value?

follow-up -- thanks to everyone who responded, i applied a mixture of all suggestions. we have "call" scripts with are either shell (unix) or bat (windows), that call centralized perl scripts. there are about 100+ call scripts (asking 'why' is a moot point...that's just how it is), so modifying the call script to pass $0/%0% was an absolute last resort option (i'd prefer to change one perl script, rather than 101 shell/bat scripts.
sadly, i am in a VERY restricted environment, such that most of the suggested modules cannot be used (getting those modules installed takes an act of congress...almost literally). anyway...the code is below (obviously custom for my enviornemnts). it doesn't do properly if manually entering the name of the bat file at a windows command prompt, but the people executing the bat file will not be executing it that way.
thank you again for everyone who provided feedback to help me find a resolution.
sub _getppid() {
    my $ppid;
    my $name;

    if ($^O =~ /^MSWin32$/i) {
        my $pid = $$;
        my $machine = "\\\\.";
        
        require Win32::OLE;
        require Win32::OLE::Variant;
    
        # WMI Win32_Process class
        my $class = "winmgmts:{impersonationLevel=impersonate}$machine\\Root\\cimv2";
        if (my $wmi = Win32::OLE-> GetObject($class)) {
        	my $inc = 0;
        	while ($inc < 3) {
        		my $proc = $wmi->Get(qq{Win32_Process.Handle="$pid"});
        		if ($proc) {
    	            $ppid = $proc->{ParentProcessId} if ($proc->{ParentProcessId}!=0);
	                $name = $proc->{CommandLine} if ($proc->{ParentProcessId}!=0);
	                $pid = $ppid;
	                ++$inc;
        		}
            }
        }
        $name =~ s/cmd \/c ""(.*)" "/$1/;
    }
    else {
        $ppid = getppid();
		$name = `ps -o args -p $ppid`;
		chomp($name);
		$name = (split(/\n+/, $name))-1;
		$name =~ s/\/bin\/ksh (.*)/$1/;
    }
    
    return ($ppid, $name);
}

Replies are listed 'Best First'.
Re: caller of perl script
by moritz (Cardinal) on Jun 11, 2010 at 14:12 UTC
    As you have observed, caller works only inside a Perl process.

    With getppid you can the the PID of the parent process, which is often also the calling process.

    Depending on your operating systems, there are probably ways to map that PID to a process name, which might or might not be related to the caller's name.

    I'm curious, why do you need that? Maybe there's another way to achieve your goal in a different way.

      moritz: OP mentioned a .BAT file. Is getppid implemented on Windows?

      OP: If you have any control over the calling script, could you modify it to leave tracks in an environment variable?

        moritz: OP mentioned a .BAT file. Is getppid implemented on Windows?

        Concluding the operating system from a file extension is a fragile business. I think that mono uses .exe executables on linux.

        Also there are a lot of different systems that use .BAT - the difference between DOS 6.0 and Windows 7 are striking.

        Perl 6 - links to (nearly) everything that is Perl 6.
        OP mentions both bat and shell, which likely means both Windows and Unix. most of the responses reference Windows. sadly, modules for Windows (e.g. Windows::...) can't be used in Unix. it's also likely that wild and crazy modules (though we love 'em) may not be an option. OP could be in a locked down environment where they've got to use what they've got.
Re: caller of perl script
by BrowserUk (Patriarch) on Jun 11, 2010 at 17:27 UTC

    If you know it is going to be started from one of these two places, why not have them pass that information in to you?

    It is relatively simple (on win, with the right module) to get the parent process id and name, but that isn't going to be very helpful as the name will be cmd.exe! And the command line will be either blank, or however that copy of cmd was started.

    This because .bat files are processed by the current running instance of cmd.exe, not a new instance, so the name of the .bat file is never a part of any command line, unless it was start as cmd /c the.bat

Re: caller of perl script
by ikegami (Patriarch) on Jun 11, 2010 at 19:24 UTC

    I'm curious as to why you need this. This could be an XY Problem.

    which is called by either a shell script or a bat file.

    Batch files are scripts for the command or cmd shell, meaning they are shell scripts.

Re: caller of perl script
by youwin (Beadle) on Jun 11, 2010 at 22:37 UTC
    I looked into how to get process hierarchy and argument data on windows, and heres a script that will print out a forest of process nodes, showing the arguments each was started with:
    use Win32::ToolHelp ':all'; use Win32::Process::CommandLine; use Tree; my @nodes; for (GetProcesses) { my %h; @h{qw/n_refs pid hid mid n_threads ppid prio flags name/} = @$_; Win32::Process::CommandLine::GetPidCommandLine($h{pid}, $h{args}); push @nodes, Tree->new(\%h); } my @forest; while (my $node = shift @nodes) { my ($parent) = grep {$_->value->{pid} == $node->value->{ppid}} @nodes, map $_->traverse, @forest; $parent ? $parent->add_child($node) : push @forest, $node; } for (map $_->traverse, @forest) { printf "%s%s\n", " " x $_->depth, $_->value->{args} || $_->value->{name}; }

      Here's a somewhat easier way:

      #! perl -slw use strict; use Win32::Process::Info;; my $pi = new Win32::Process::Info;; my @info = $pi->GetProcInfo( $$ ); my $ppid = $info[0]{ ParentProcessId }; @info = $pi->GetProcInfo( $ppid ); print "Parent process name : ", $info[0]{ ExecutablePath }; print "Command line: ", $info[0]{ CommandLine };

      But as I explained earlier, it's not very useful for the OPs stated requirement as it always gives the same information:

      c:\test>type junk.bat dad c:\test>junk.bat c:\test>dad Parent process name : C:\Windows\System32\cmd.exe Command line: "C:\Windows\System32\cmd.exe"

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
Re: caller of perl script
by cdarke (Prior) on Jun 12, 2010 at 13:19 UTC
    The simplest way is to get the bat file to pass %0% on the command-line to your perl script.

    Getting the parent PID on Windows is not easy, but BrowserUk showed the way. To get the name of the bat file you need the Window Title, which is not included in the information returned from Win32::Process::Info. However, the tasklisk \V command does give it, the calling bat file is the final field (play with tasklist \V on cmd.exe if you are not familiar with the output). Here is my extension to BrowserUk's code:
    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Win32::Process::Info; my $pi = new Win32::Process::Info;; my @info = $pi->GetProcInfo( $$ ); my $ppid = $info[0]{ ParentProcessId }; # cdarke's addition from here # tasklist outputs a couple of lines of title junk first my ($tl_out) = (qx(tasklist /v /fi "PID eq $ppid"))[-1]; # The bat file name is the last "column" my $parent = (split /\s+/,$tl_out)[-1]; # $parent should now give the bat file name print "$parent\n";
A reply falls below the community's threshold of quality. You may see it by logging in.