A="foo" B="some other data" C="appending $A" D=" some list with more data and $B " #### #!/bin/bash - . foo.conf export A export B export C export D ./foo.pl #### #!/usr/bin/perl my %exp_vars = ( A => 'SCALAR', B => 'SCALAR', C => 'SCALAR', D => 'ARRAY', ); my $env; foreach my $var (keys %exp_vars) { my $type = $exp_vars{$var}; my $val = lc $var; if (! defined $ENV{$var} or length($ENV{$var}) <= 1) { push @out, "# [$var] not exported"; next; } if ($type eq 'ARRAY') { @{$env->{$val}} = ($ENV{$var} =~ m/(?:\s+)?(\S+)/g); } elsif ($type eq 'SCALAR') { $env->{$val} = $ENV{$var}; } } #### #!/bin/env perl use strict; use warnings; use Data::Dumper; sub parse_sh { my ($data) = @_; my $r; $data =~ s/((?: *#.*?)?\n+)/\n/gs; # Remove comment lines $data =~ s/(\n+)/\n/gs; # Remove blank lines $data =~ s/ *(\S+)=("?)(.*?)\2 *\n/$r->{lc($1)}="$3"/gse; return $r; } sub parse_sh_maint { my ($r, $noset) = @_; # Interpolate variable names my $max_passes = 5; my $not_substituted; for (1 .. $max_passes ) { for my $k (keys %$r) { next unless (ref(\$r->{$k}) eq 'SCALAR'); $r->{$k} =~ s|\$(\w+)| $r->{lc($1)} // do{ "\$$1" } |ge; $not_substituted += $r->{$k} =~ tr/$/$/; } last unless $not_substituted; } # Remove remaining variables and make variables with spaces and newlines arrays for my $k (keys %$r) { $r->{$k} =~ s/\$\S+ *?//gs; # Remove remaining variables $r->{$k} =~ s/\s+/\n/gs; # Remove leading spaces if (not grep { $_ eq $k } @$noset) { my %dedupe = map { $_ => 1 } split(/ |\n/, $r->{$k}); $r->{$k} = (); @{$r->{$k}} = keys %dedupe; } } return $r; } open(my $fh, "<", $ARGV[0]); my $content = do { local $/ = <$fh> }; my $stuff = parse_sh_maint(parse_sh($content), [qw/scalar1 scalar2/]); print "Vars: " . Dumper($stuff), "\n";