Search code examples
bashperlsyntax-errorruntime-errorembedded-script

How to see syntax errors reported with actual line numbers in the parent script when Perl is embedded within shell script?


For no justifiable reason at all, I have a pretty substantial Perl script embedded within a Bash function that is being invoked within an autoenv .env file.

It looks something like this:

perl='
    $inverse = "\e[7m";
    $invoff  = "\e[27m";
    $bold    = "\e[1m";
    ⋮
'

perl -e "$perl" "$inputfile"

I understand that standalone Perl scripts and the PATH variable are a thing, and I understand that Term::ANSIColor is a thing. This is not about that.

My question is, if there's a syntax error in the embedded Perl code, how can I get Perl to report the actual line number within the parent shell script?

For example, say the perl= assignment occurs on line 120 within that file, but there's a syntax error on the 65th line of actual Perl code. I get this:

syntax error at -e line 65, near "s/(#.*)$/$comment\1$endcomment/"
Execution of -e aborted due to compilation errors.

…but I want to see this (the actual line number in the parent script) instead:

syntax error at -e line 185, near "s/(#.*)$/$comment\1$endcomment/"

Things I've tried (that didn't work):

  • assigning to __LINE__
    • don't even know why I thought that would work; it's not a variable, it's a constant, and you get an error stating the same
  • assigning to $. ($INPUT_LINE_NUMBER with use English)
    • I was pretty sure this wasn't going to work anyway, because this is like NR in Awk, and this clearly isn't what this is for

Solution

  • As described in perlsyn, you can use the following directive to set the line number and (optionally) the file name of the subsequent line:

    #line 42 "file.pl"
    

    This means that you could use

    #!/bin/sh
    
    perl="#line 4 \"$0\""'
    warn("test");
    '
    
    perl -e "$perl"
    

    Output:

    $ ./a.sh
    test at ./a.sh line 4.
    

    There's no clean way to avoid hardcoding the line number when using sh, but it is possible.

    #!/bin/sh
    
    script_start=$( perl -ne'if (/^perl=/) { print $.+1; last }' -- "$0" )
    perl="#line $script_start \"$0\""'
    warn("test");
    '
    
    perl -e "$perl"
    

    On the other hand, bash provides the current line number.

    #!/bin/bash
    
    script_start=$(( LINENO + 2 ))
    perl="#line $script_start \"$0\""'
    warn("test");
    '
    
    perl -e "$perl"