0

The following shell script is working fine while I execute it from a Linux prompt, but when it's called by a Unix service (e.g. inetd, etc.) it doesn't recognize the $ACT_NUM variable inside the Perl script.

export ACT_NUM=$1
perl -pi -e '$v=$ENV{ACT_NUM};s/TRIGGER_VALUE" value="\d*"/TRIGGER_VALUE" value="$v"/>//' soa_triggering.xml

The soa_triggering.xml content file is

<ins:Parameter name="TRIGGER_VALUE" value="1"/>
Borodin
  • 126,100
  • 9
  • 70
  • 144
Fernando
  • 1
  • 1
  • 2
    Don't test code with `i` flag. – 123 Jul 05 '16 at 12:59
  • 4
    Going to need some more detail - where does `$1` come from? If this is `XML` - and it looks like it is - then this is the wrong way of tackling the problem anyway. – Sobrique Jul 05 '16 at 13:00
  • this is a shell script which is called by an asterisk hook. anyway, $1 is string a parameter. In fact, I print $1 to standard output without any problem. I assume there's something wrong insider perl script. – Fernando Jul 05 '16 at 13:20
  • Possibly. What outcome do you get when you run it? I'm going to take a guess that nothing changes, because your regex isn't matching. – Sobrique Jul 05 '16 at 13:23
  • My linux knowlege is vague, but as far as I understand: you set ENV variable for your user. And asterisk starts script from it's own user, which has no this variable. – Alexandr Evstigneev Jul 05 '16 at 13:32
  • 3
    Is the `export ACT_NUM=$1` actually **part of the shell script**, or are you executing it somewhere else and expecting the value to be inherited? If the latter, where *exactly* is the somewhere else, and what's its process-tree relationship to the invocation point? – Charles Duffy Jul 05 '16 at 13:47

2 Answers2

6

I think your problem is more fundamental than expansion of $1 - I'm going to hazard a guess that the regex isn't matching - because:

$v=$ENV{ACT_NUM};s/TRIGGER_VALUE" value="\d*"/TRIGGER_VALUE" value="$v"/>//

Is actually broken syntax - you're using / as your regex separator, but you're also trying to include it in your pattern.

So if you actually run this code you get:

Useless use of numeric gt (>) in void context 

So perhaps you need to consider escaping some of your meta characters:

This works:

#!/usr/bin/env perl
use strict;
use warnings;

$ENV{ACT_NUM} = 1;

while ( <DATA> ) {
    my $v=$ENV{ACT_NUM};
    s/TRIGGER_VALUE\" value=\"\d*\"/TRIGGER_VALUE\" value=\"$v\"/;
    print;
} 

__DATA__
<ins:Parameter name="TRIGGER_VALUE" value="1"/>

But really - messing around with XML using regular expressions is an inherently bad idea.

You also might want to double check that that environment actually is being propagated. If you print $v (or $ENV{ACT_NAME}) does it actually work?

So how about instead:

#!/usr/bin/env perl
use strict;
use warnings;

use XML::Twig;

my $twig = XML::Twig->new(
   twig_handlers => {
      'ins:Parameter[@name="TRIGGER_VALUE"]' =>
        sub { $_->set_att( 'value', $ENV{ACT_NUM} ) }
   }
);
$twig->parse(
   do { local $/; <> }
)->print;
Community
  • 1
  • 1
Sobrique
  • 52,974
  • 7
  • 60
  • 101
  • Will your code at the end work without defining the `ins` namespace to `XML::Twig`? (Honest question, as it's a library I'm unfamiliar with, but for the majority of the libraries I *am* familiar with that answer would be "no"). – Charles Duffy Jul 05 '16 at 14:44
  • Yup. `XML::Twig` by default ignores namespacing. (I call that a useful feature in _most_ XML cases) – Sobrique Jul 05 '16 at 14:46
  • Heh. I'm a loud proponent of namespaces in general -- being able to edit a *template* that generates a SVG document directly in Inkscape, despite it not knowing the template language (but knowing to leave things in namespaces it doesn't understand alone) is a beautiful thing -- but I'll admit that here, that's a useful feature. – Charles Duffy Jul 05 '16 at 14:49
  • You can do namespacing if you need to - it's just unlike most, if you just ignore it, it works fine. (which honestly, is the vast majority of times I'm parsing XML) – Sobrique Jul 05 '16 at 15:08
2

Regular expressions are insufficiently expressive to parse XML. For instance, your same value could be written out over three lines with the parameter order reversed, as:

<ins:Parameter
 value="1"
 name="TRIGGER_VALUE"/>

...or with numerous other variances (unexpected whitespace, &c). Trying to cover the corner cases -- which also include distinguishing content in CDATA and comments from actual tags -- is potentially sanity-impacting.


If you're editing XML, use an actual XML parser. One good choice is XMLStarlet:

xmlstarlet ed -i \
  -u '//*[@name="TRIGGER_VALUE"]/@value' \
  -v "$ACT_NUM" \
  soa_triggering.xml

If you know what the ins namespace points to, you can do better:

# Replace http://example.com/ins-namespace with actual value
# from xmlns:ins="..." earlier in your document
xmlstarlet ed -i -N "ins=http://example.com/ins-namespace" \
  -u '//ins:Parameter[@name="TRIGGER_VALUE"]/@value' \
  -v "$ACT_NUM" \
  soa_triggering.xml
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441