1

I'm working on converting Perl cgi script to Python.

Inside the script, I'm came across this below code which I didn't understand.

Help me figure this out.

my $p = $0;
$p =~ s|.*/||;

I understood that my $p = $0; assigns the absolute path+name of the script file to variable p.

and the second line is going to perform regex substitution and give only the file name without path.

But what I didn't understand are those |.*/|| after s.

I searched many resources but didn't understand what it does.

What are those things actually going to do to give me only the script name?

Anudeep
  • 185
  • 1
  • 1
  • 8

2 Answers2

5

The Perl substitution operator looks like this:

s/PATTERN/STRING/

But it's also one of those Perl operators that let you choose your own delimiters. So another way to write that is:

s|PATTERN|STRING|

And that's the version that you're seeing here. Your pattern is .*/ and the replacement string is empty. So your substitution means "find as many (non-newline) characters as possible that are followed by a slash and replace them with nothing". Looking at that alongside the assignment in the line above it (which uses $0 - the path of the program) it looks like we have code that removes the directories from the front of a path and just leaves the filename.

That is, if you start with a program name of /usr/local/bin/my_program, then $p will end up just containing my_program.

The better way to write that in Perl is to use the File::Basename module. I'd be astonished if Python didn't have something similar.

use File::Basename;

my $p = basename($0);
Dave Cross
  • 62,464
  • 3
  • 46
  • 83
  • 1
    A side comment for the fine answer -- `$0` won't give the path but it rather "[_Contains the name of the program_](https://perldoc.perl.org/perlvar.html#$0)." (At least, it's not guaranteed to give the path.) The problem with it is (I think) that it doesn't guarantee to return only the name either :) There's also `FindBin::$RealScript`. One of ways in Python is `os.path.basename(__file__)` – zdim Oct 11 '18 at 17:14
1

Given that the special variable $0 does not contain line feeds - the s|.*/|| regexp actually removes .* (everything) before the last (and including last) / from your string. So you can cut the path and keep only the filename (everything after the last /).

There are different delimiters for substitution. It can be s/// or whatever. Anyway you need to read about the perl substitution.

FYI. Also keep in mind, that .*? means non-greedy regexp. So it will match anything before the first /.

z0lupka
  • 196
  • 3
  • 16
  • @z0lupka `$0` can be set to anything (and thus, `__FILE__` may be a better choice for certain cases but the use case in the OP is unclear). `perl -E'$0 = "foo\nbar"; say $0'` – Grinnz Oct 11 '18 at 16:55
  • 1
    @Grinnz., `__FILE__` can be set to anything too via the `#line` pragma – ikegami Oct 11 '18 at 20:53
  • @ikegami It would require some black magic to do such without being apparent to the code relying on it (also it's far less common and usually used to fix `__LINE__`), whereas `$0` can be globally altered by anything at any time... – Grinnz Oct 11 '18 at 21:00
  • 1
    @Grinnz Do you think it makes sense to consider all possible conditions when you write cgi-script with certainly well-defined invocation semantics? And when you are sure that you don't use such obscene pre-defining like '$0 = "foo\nbar";` – z0lupka Oct 12 '18 at 08:04