7

In my Perl script I get strings of file paths that may contain environment variables, e.g. $FONTS/test.ttf or $TMP/file.txt.

I now want to open those files like this:

open my $handle, "<$filename" or die $!;

How can I now expand the environment variables prior to the open call, as e.g. the bash shell would do?

daxim
  • 39,270
  • 4
  • 65
  • 132
Fabian
  • 5,476
  • 4
  • 35
  • 46
  • 6
    you really want to use a 3 arg open here to prevent a security vulnerability. – xenoterracide Aug 16 '10 at 14:22
  • 1
    Might as well provide links: http://stackoverflow.com/questions/318789/whats-the-best-way-to-open-and-read-a-file-in-perl http://stackoverflow.com/questions/1479741/why-is-three-argument-open-calls-with-lexical-filehandles-a-perl-best-practice – daxim Aug 16 '10 at 14:44

4 Answers4

13

If the environmental variables are set, you can use a simple substitution:

$filename =~ s/\$(\w+)/$ENV{$1}/g;
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • 2
    Neat and succinct - and, as ever, error processing will screw it up. Specifically, if one of the environment variables referenced is not set, you will at best get an unexpected path; you may also get warnings about accessing undefined variables from Perl. Although there are other shell notations (like `${TMP}`), they are seldom used in contexts where that will be a problem. – Jonathan Leffler Aug 16 '10 at 14:14
  • @Jonathan, the standard behavior for bash is to substitute the empty string for a missing variable, so this code does handle that error condition in the way the OP asked. I agree on the alternate shell notations I use `${FOO:?}` quite a lot in scripts to force bash to throw errors on unset variables. – Ven'Tatsu Aug 16 '10 at 21:48
  • I updated this to `s/\$\{?(\w+)\}?/$ENV{$1}/` to support the alternate curly brace format. – Sherwin F Feb 08 '19 at 20:53
3

Why not just do this:

$filename =~ s/\$\{(\w+)\}/$ENV{$1}/g;
$filename =~ s/\$(\w+)/$ENV{$1}/g;
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
0

Well the following is considerably more verbose, but it seems to correctly handle both $FOO and ${FOO} syntax.

#!/usr/bin/env perl

use warnings;
use strict;

my $filename = $ARGV[0];
print(" GIVEN: <$filename>\n");
my $expanded = '';
my @parts = split(/(\$\w+)|(\${\w+})/, $filename);
foreach my $seg (@parts)
  {
    next if (not defined($seg));
    $seg = ($ENV{$1} || '')  if ($seg =~ m/\${?(\w+)}?/);
    $expanded .= $seg;
  }

print("IS NOW: <$expanded>\n");
print(`echo "  ECHO: <$filename>"`);  # How the shell did it.

Here's a sample run...

$ ./expand '$TERM ---${TERM}--- ===${FOO}=== $FOO'
 GIVEN: <$TERM ---${TERM}--- ===${FOO}=== $FOO>
IS NOW: <xterm-color ---xterm-color--- ====== >
  ECHO: <xterm-color ---xterm-color--- ====== >
$

In this version, undefined symbols are replace with an empty string, like the shell. But in my apps I'd rather leave the unexpanded invalid symbol in place making it easier to see what went wrong. Also, if you screw up your bracket matching, the shell will give you a "bad substitution" error, but here we'll just return the string with the broken symbol still in place.

KillerRabbit
  • 173
  • 1
  • 8
0

sorry but you are wrong.

to work in perl with SHELL variables, the correct syntax is : $ENV{variable}

example with a colored GCC output function :

n=$(tput setaf 0)
r=$(tput setaf 1)
g=$(tput setaf 2)
y=$(tput setaf 3)
b=$(tput setaf 4)
m=$(tput setaf 5)
c=$(tput setaf 6)
w=$(tput setaf 7)
N=$(tput setaf 8)
R=$(tput setaf 9)
G=$(tput setaf 10)
Y=$(tput setaf 11)
B=$(tput setaf 12)
M=$(tput setaf 13)
C=$(tput setaf 14)
W=$(tput setaf 15)
END=$(tput sgr0)

colorgcc()
    {
    perl -wln -M'Term::ANSIColor' -e '
    m/not found$/ and print "$ENV{N}$`$ENV{END}", "$&", "$ENV{END}"
    or
    m/found$/ and print "$ENV{N}$`${g}", "$&", "$ENV{END}"
    or
    m/yes$/ and print "$ENV{N}$`${g}", "$&", "$ENV{END}"
    or
    m/no$/ and print "$ENV{N}$`$ENV{END}", "$&", "$ENV{END}"
    or
    m/undefined reference to/i and print "$ENV{r}", "$_", "$ENV{END}"
    or
    m/ Error |error:/i and print "$ENV{r}", "$_", "$ENV{END}"
    or
    m/ Warning |warning:/i and print "$ENV{y}", "$_", "$ENV{END}"
    or
    m/nsinstall / and print "$ENV{c}", "$_", "$ENV{END}"
    or
    m/Linking |\.a\b/ and print "$ENV{C}", "$_", "$ENV{END}"
    or
    m/Building|gcc|g\+\+|\bCC\b|\bcc\b/ and print "$ENV{N}", "$_", "$ENV{END}"
    or
    print; '
    }
scavenger
  • 11
  • 1