..

envsubst 在 perl 的实现

代码来源: https://metacpan.org/source/PEVANS/String-Expand-0.04/lib/String/Expand.pm

#!/usr/bin/env perl
my $VARNAME_MATCH = qr/\$([A-Z_][A-Z0-9_]*|\{.*?\})/;

sub expand_one_var($$)
{
   my ( $var, $vars ) = @_;

   # Chop off delimiting {braces} if present
   $var =~ s/^\{(.*)\}$/$1/;

   unless( defined $vars->{$var} ) {
      die "Unknown variable '$var'";
   }

   return $vars->{$var};
}

sub expand_string($$)
{
   my ( $str, $vars ) = @_;

   $str =~ s{\\([\\\$])|$VARNAME_MATCH}
            {     $1  or expand_one_var( $2, $vars )}eg;

   return $str;
}

sub expand_strings_inner($$$$);

sub expand_strings_one_var($$$$)
{
   my ( $var, $strs, $overlay, $done ) = @_;

   # Chop off delimiting {braces} if present
   $var =~ s/^\{(.*)\}$/$1/;

   if( exists $strs->{$var} ) {
      return $strs->{$var} if( $done->{$var} );
      # Detect loops
      if( exists $done->{$var} ) {
         die "Variable loop trying to expand '$var'";
      }
      $done->{$var} = 0;
      expand_strings_inner( $strs, $overlay, $var, $done );
      return $strs->{$var};
   }

   return $overlay->{$var} if( exists $overlay->{$var} );

   die "Unknown variable '$var'";
}

sub expand_strings_inner($$$$)
{
   my ( $strs, $overlay, $v, $done ) = @_;

   if( $strs->{$v} =~ m/[\\\$]/ ) {
      $strs->{$v} =~ s{\\([\\\$])|$VARNAME_MATCH}
                      {     $1  or expand_strings_one_var( $2, $strs, $overlay, $done )}eg;
   }

   $done->{$v} = 1;
}

sub expand_strings($$)
{
   my ( $strs, $overlay ) = @_;

   # 0: a variable expansion is in progress
   # 1: value has been correctly expanded
   my %done;

   foreach my $v ( keys %$strs ) {
      expand_strings_inner( $strs, $overlay, $v, \%done );
   }
}

my $str = "hello $USER";
print expand_string($str,%ENV) . "\n";