use strict; use warnings; use Shell::Parser; use Data::Dumper; sub parse_shellscript_from_fh { my ($fh) = @_; my %vars; my $parser = new Shell::Parser syntax => 'bash', handlers => { assign => sub { my ($self, %args) = @_; my ($name, $value) = $args{token} =~ /^\s*(\w+)\s*=\s*\"(.*)\"\s*$/s; if ( defined $value ) { #-- multi-line/array? $value =~ s/\n//msg; $vars{$name} = $value; } else { warn "No value for variable ", ($name//'?'), "\n"; } } }; #-- parsing my $text = join("", <$fh>); $parser->parse( $text ); $parser->eof; #-- rudimentary variable interpolation my $max_passes = 5; my $not_substituted; for (1 .. $max_passes ) { $not_substituted = 0; foreach my $varname ( keys %vars ) { $vars{$varname} =~ s|\$(\w+)| $vars{$1} // do{ "\$$1" } |ge; $not_substituted += $vars{$varname} =~ tr/$/$/; } last unless $not_substituted; } die "Recursion or too few parses ... " if $not_substituted; return \%vars; } print "Vars: ", Dumper( parse_shellscript_from_fh( *DATA ) ), "\n"; __DATA__ A="foo" B="some other data" C="appending $A" D=" some list with more data and $B " X=<## No value for variable ? No value for variable ? Vars: $VAR1 = { 'A' => 'foo', 'D' => ' some list with more data and some other data ', 'C' => 'appending foo', 'B' => 'some other data' };