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

Patient monks,

I come again with another in a continuing line of questions -- How do I find the path of the script running as a Win service? I have the following scenario.

  1. foo.pl is converted to foo.exe using pp.
  2. foo.exe is then copied to another computer, say, under E:\programs\foo\foo.exe
  3. The Win command sc is used to create a service called foo.
I want foo, the service, to figure out the path of foo.exe.

Here is what I have tried --

File::Spec->rel2abs(curdir()), $FindBin::Bin, Cwd->cwd() all suffer from the same problem thusly: the path returned is the ostensible path to the service, of the form "C:\Windows\System32", not "E:\programs\foo", the path to foo.exe that is being run as that darned service.

I finally implemented the following solution --

@path = split(/\\/, $0); pop(@path); $path = join('\\', @path);
The above works, however, is there a less ugly solution?

I have a related question --

BEGIN { my $foo = 'bar'; } print $foo;
doesn't work for obvious reasons. However, neither does
my $foo = 'bar'; BEGIN { print $foo; }
Why? Does BEGIN have its own namespace? How can I declare something once (like the $path to the script I am trying to figure out above), and have it be picked up in the BEGIN block as well as in the rest of the script or any other module that I might use in the script?

Many thanks.

Replies are listed 'Best First'.
Re: finding the path of the script running as Win service
by borisz (Canon) on Jan 05, 2005 at 22:18 UTC
    the first part could be written as
    ( our $path = $0 ) =~ s/\\[^\\]*$//;
    fro the second part, this works:
    my $foo; BEGIN { $foo = 'bar'; }
    Boris
      wow! What is going on here? ( our $path = $0 ) =~ s/\\[^\\]*$//;

      I can't even grok the above. That said, wrt the second part, I want to really do

      ( our $path = $0 ) =~ s/\\[^\\]*$//; BEGIN { print $path; }
      Why does $path declared before the BEGIN block not be visible inside the BEGIN block?

        $0 is the special variable that "contains the name of the program being executed." It's nmemonic is "$PROGRAM_NAME".

        'our $path = $0' assigns $0 to $path. This is a legal request, even as the lvalue of a =~ operator. And the s/// operator then acts upon $path.

        Read it in this order:

        1. Declare $path.
        2. Assign $0 to $path
        3. Bind $path to a substitution operator.
        4. Substitute, matching on the last '\' character, followed by any amount (or nothing at all) of non '\' characters. Replace with nothing. Essentially, this strips away the filename and final '\' character, on a system where '\' is the path separator.

        Actually, the regexp is a little fragile, and you might be better off using File::Basename's dirname() funtion.

        To answer your second question, the BEGIN{} block is executed at an earlier stage, during compilation time instead of runtime. That means that even though the compiler has seen ( our $path = $0 ) before it sees the BEGIN{} block, it executes the begin block before the assignment to $path takes place. Note also that if use strict; is in effect, our enforces a sort of lexical scoping on the usage of $path. That's not really an issue here, but worth mentioning.


        Dave

        what you might want is:
        BEGIN { ( our $path = $0 ) =~ s/\\[^\\]*$//; }
        Now you can use $path everywhere.
        The BEGIN block is done at compiletime. The rest is done at runtime. Perl compiles your script and runs the compiled then.
        Read perldoc perlmod.
        Boris
Re: finding the path of the script running as Win service
by Joost (Canon) on Jan 05, 2005 at 22:22 UTC
    Parsing $0 might not be very pretty, but if it works, why not use it?

    Anyway:

    my $foo = 'bar'; BEGIN { print $foo; }
    Your my statement is executed in 2 different stages: the declaration is run at "compile time" (as soon as the compiler sees it), and the assignment is run at "run time".

    Begin blocks also run at compile time, so the order of execution of the above code would be something like:

    my $foo; print $foo; $foo = 'bar';
    This also explains why the code also runs under strict: BEGIN does not have its own namespace and lexical variables don't use namespaces anyway.

      thanks for the explanation re compile and run times. Now, if someone to be kind enough to explain what is Boris doing in the above regex... I don't like magic.

      ;-)

        The regex matches a backslash (\\) followed by any number of non-backslash characters ([^\\]*) followed by end-of-string ($). This forces the matched backslash to be the last backslash in the string, so the matched non-backslash characters are the filename ("foo.exe"). The matched characters ("\foo.exe") are replaced by the empty string, which gives the directory containing the script file ("E:\programs\foo").

        The OP accomplished this less efficiently by splitting the string on backslashes (("E:","programs","foo","foo.exe")), popping the last element of the array ("foo.exe", leaving ("E:","programs","foo")), and rejoining the array with backslashes ("E:\programs\foo").