Search code examples
pythonperlvirtualenvshebang

How does this Perl shebang work? (For someone not coming from Perl)


Could someone explain how this shebang works?

#!/usr/bin/perl -e$_=$ARGV[0];exec(s/\w+$/python3/r,$_)

I've seen it posted twice here but not coming from Perl it looks like magic to me. I ask as I would like to adjust the directory to a python environment relative to the script.

i.e #!../env/bin/python3 to (I'm just guessing here) #!/usr/bin/perl -e$_=$ARGV[0];exec(s/\w+$/env/bin/python3/r,$_)

Edit: I am trying to execute a simple ''Hello world" program.

#!/usr/bin/perl -e'$_=$ARGV[0];exec(s{\w+$}{exploit-env/bin/python3}r,$_)'

###############################

def main():
    print('Hello world')

###############################

if __name__ == '__main__':
    main()

Solution

  • Shebang handling isn't consistent across all systems[1], but your system apparently apparently executes what the following shell command would execute (assuming the file containing the shebang is /path/to/script):

    /usr/bin/perl -e'$_=$ARGV[0];exec(s/\w+$/python3/r,$_)' /path/to/script
    

    (The path to the script might not be an absolute path —it might be relative to the current directory— but doesn't matter here.)

    The script produces /path/to/python3 from /path/to/script (by replacing the trailing "word" characters, which include letters, digits and underscore, but not /), then evaluates

    exec('/path/to/python3', '/path/to/script')
    

    The replaces the program running in the current process with a Python interpreter, passing the path to the script as an argument.


    If I read between the lines correctly, you want to to run /path/to/../env/bin/python3 instead of /path/to/python3. In order to achieve that, use either of the following:

    #!/usr/bin/perl -e$_=$ARGV[0];exec(s/\w+$/..\/env\/bin\/python3/r,$_)
    

    or

    #!/usr/bin/perl -e$_=$ARGV[0];exec(s{\w+$}{../env/bin/python3}r,$_)
    

    / needs to be escaped by \ when / is used as the delimiter (first solution), but we can change the delimiter to produce a more readable result (second solution).


    That shebang you presented causes the arguments to be absorbed. Replace the $_ with @ARGV to pass them on.

    #!/usr/bin/perl -e$_=$ARGV[0];exec(s{\w+$}{../env/bin/python3}r,@ARGV)
    

    1. At least historically, some systems treat the entire sequence that follows #! as the path (i.e. no arguments allows), and some have very strict limits as to the length of the path.