How can I disable variable interpolation with the Perl substitution operator?
Asked Answered
N

8

5

I'm trying to replace a particular line in a text file on VMS. Normally, this is a simple one-liner with Perl. But I ran into a problem when the replacement side was a symbol containing a VMS path. Here is the file and what I tried:

Contents of file1.txt:

foo
bar
baz
quux

Attempt to substitute 3rd line:

$ mysub = "disk$data1:[path.to]file.txt"
$ perl -pe "s/baz/''mysub'/" file1.txt

Yields the following output:

foo
bar
disk:[path.to]file.txt
quux

It looks like Perl was overeager and replaced the $data1 part of the path with the contents of a non-existent variable (i.e., nothing). Running with the debugger confirmed that. I didn't supply /e, so I thought Perl should just replace the text as-is. Is there a way to get Perl to do that?

(Also note that I can reproduce similar behavior at the linux command line.)

Nollie answered 1/10, 2012 at 18:2 Comment(7)
In Perl, you can disable variable-interpolation within s/// by using the delimiter ' instead of / (e.g. s'baz'disk$data1:[path.to]file.txt'). Unfortunately, judging from what you've posted, it looks like VMS uses ' to perform its own variable substitution? So, it might be easier-said-than-done to get the single-quotes into the Perl script. :-/Infancy
Yep, I tried various flavors of that before posting here. You need exactly two '' and one ' to do variable substitution on VMS, and its interpreter is easily confused.Nollie
When in doubt, use a script file instead of a one-liner. And use use strict; use warnings; If you had defined my $mysub = 'disk$data1:[path.to]file.txt' (note the single quotes to avoid variable interpolation) or even my $mysub = $ENV{mysub] (if you insist on using shell variables) inside a perl script, you would not have this problem.Gallant
@TLP: Actually, $ENV{mysub} would completely address the problem; that's the minimal fix. Instead of trying to disable the interpolation in Perl, the OP should disable the interpolation in VMS, by writing perl -pe "s/baz/$ENV{mysub}/" file1.txt!Infancy
mysub would have to be defined as a logical in that case, but yep, I think you're right.Nollie
@Infancy Perhaps that should be an answer then. I added one.Gallant
@TLP: Thanks, I've +1'd it. :-)Infancy
W
4

With just the right mix of quotes and double quotes you can get there:

We create a tight concatenation of "string" + 'substitute symbol' + "string". The two double quoted strings contain single quotes for the substitute. Contrived... but it works.

$ perl -pe "s'baz'"'mysub'"'" file1.txt

That's a DCL level solution.

For a Perl solution, use the q() operator for a non-interpolated string and execute that. Stuff the symbol into the parens using simple DCL substitution. This is my favorite because it (almost) makes sense to me and does not get too confusing with quotes.

$ perl -pe "s/baz/q(''mysub')/e" file1.txt
Wapiti answered 1/10, 2012 at 23:6 Comment(0)
G
4

As ruakh discovered when reading my comment, the problem of perl interpolation can be solved by accessing the %ENV hash, rather than using a shell variable:

perl -pwe "s/baz/$ENV{mysub}/" file1.txt

Added -w because I do not believe in not using warnings even in one-liners.

Gallant answered 1/10, 2012 at 18:40 Comment(1)
Yes, this works (just tried it). You need to use a logical instead of a symbol: define mysub "disk$data1:[path.to]file.txt".Nollie
W
4

With just the right mix of quotes and double quotes you can get there:

We create a tight concatenation of "string" + 'substitute symbol' + "string". The two double quoted strings contain single quotes for the substitute. Contrived... but it works.

$ perl -pe "s'baz'"'mysub'"'" file1.txt

That's a DCL level solution.

For a Perl solution, use the q() operator for a non-interpolated string and execute that. Stuff the symbol into the parens using simple DCL substitution. This is my favorite because it (almost) makes sense to me and does not get too confusing with quotes.

$ perl -pe "s/baz/q(''mysub')/e" file1.txt
Wapiti answered 1/10, 2012 at 23:6 Comment(0)
C
1

Haven't even seen a VMS system in decades, but ... escape your sigil?

$ mysub = "disk\$data1:[path.to]file.txt"

or maybe

$ mysub = "disk\\$data1:[path.to]file.txt"

?

Callison answered 1/10, 2012 at 18:10 Comment(2)
+1, that will work for this contrived case. But what if I don't control mysub?Nollie
Not familiar with how the VMS shell works - TLP's answer is something I would try.Callison
A
1

For sh or a derivative, I'd use

perl -pe'BEGIN { $r = shift(@ARGV) } s/baz/$r/' "$mysub" file1.txt

Otherwise, you have to somehow covert the value of mysub into a Perl string literal. That would be more complicated. Find the equivalent for your shell.

Edit by OP:

Yes, this works. The VMS equivalent is

perl -pe "BEGIN { $r = shift(@ARGV) } s/baz/$r/" 'mysub' file1.txt
Anastigmatic answered 1/10, 2012 at 18:18 Comment(0)
A
0

You have to escape the $ with \$. Otherwise Perl sees a variable reference and replaces the string $data1 with the content of $data1 before the regular expression is evaluated. As you didn't define $data1 it is, of course, empty.

Anthracite answered 1/10, 2012 at 18:6 Comment(0)
A
0

You can use the following code :

$ mysub='disk\$data1:[path.to]file.txt'
$ perl -pe 's/baz/'$mysub'/' FILE
foo
bar
disk$data1:[path.to]file.txt
quux
Accomplished answered 1/10, 2012 at 18:12 Comment(1)
The single quotes are used for variable substitution on VMS, so that won't work. It looks like it works on linux though.Nollie
G
0

You could try using a logical name instead of a DCL symbol:

$ define mysub "disk$data1:[path.to]file.txt"
$ $ perl -pe "s/baz/mysub/" file1.txt
Garner answered 24/10, 2012 at 16:27 Comment(0)
N
-1

Use sed.

Sed won't try to interpolate $data1 as a variable, so you should get what you want.

$ sed -e "s/baz/''mysub'/" file1.txt
Nollie answered 1/10, 2012 at 18:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.