#!/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";