Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 808
TheDudeAbides Avatar asked Dec 10 '22 00:12

TheDudeAbides


2 Answers

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"
like image 155
ikegami Avatar answered Jan 03 '23 11:01

ikegami


There is this useful tidbit in the perlrun man page, under the section for -x, which "tells Perl that the program is embedded in a larger chunk of unrelated text, such as in a mail message."

All references to line numbers by the program (warnings, errors, ...) will treat the #! line as the first line. Thus a warning on the 2nd line of the program, which is on the 100th line in the file will be reported as line 2, not as line 100. This can be overridden by using the #line directive. (See Plain Old Comments (Not!) in perlsyn)

Based on the bolded statement, adding #line NNN (where NNN is the actual line number of the parent script where that directive appears) achieves the desired effect:

perl='#line 120
    $inverse = "\e[7m";
    $invoff  = "\e[27m";
    $bold    = "\e[1m";
    ⋮
'
⋮
like image 25
TheDudeAbides Avatar answered Jan 03 '23 09:01

TheDudeAbides