eLore has asked for the wisdom of the Perl Monks concerning the following question:

Anyone have any syntax to parse the variables from a .kshrc or .cshrc? I seem to be unable to deal with things like: MYDIR = /home/foo fooBIN = $MYDIR/bin (I'm creating a slightly modified version of Environment::import from pg 92 of the O`Reilly black panther book...)

Replies are listed 'Best First'.
Re: Parsing variables from login scripts
by chromatic (Archbishop) on Jul 28, 2000 at 02:27 UTC
    It's not too difficult... you just need split and a regex check. Something like the following is untested but conceptually fairly sound:
    while (<INPUT>) { chomp; my ($var, $value) = split(/\s*=\s+/, $_, 2); if ($value =~ s/^\$(\w+)//) { # $value .= $env_var{$1} || ''; $value = $env_var{$1} . $value; } $env_var{$var} = $value; }
    You could make that shorter, but you could also add better checking to make it better. Jellybean actually does this in Jellybean::Config, so I know I've written it correctly at least once before.

    Update: Don't work and post. Fastolfe is correct, and I've updated my code to work.

      I've been working way too many hours to see this clearly. Here's what's not working so well, so far... Ok, let's see if I can format this correctly:
      ___DATA___ MY_DIR=~/temp/module export MY_DIR MY_BIN=$MY_DIR/bin MY_DATA=$MY_DIR/data export MY_BIN MY_DATA __temp.pl__ #! /usr/local/bin/perl require "testpack.pm"; testpack::import(); print "DIR $DIR\n"; __testpack.pm__ package testpack; sub import { $conf_file = "conf.sh"; open (FILE, $conf_file) || die "Cant open file. \n"; while (<FILE>) { if (! (/^\W/ || /^export/)) { # ign. cmnts & ws chomp; print "$_\n"; my ($var, $value) = split(/\s*=\s+/, $_, 2); if ($value =~ s/^\$(\w+)//) { $value .= $env_var{$1} || ''; } $env_var{$var} = $value; my ($caller_package) = caller; *{"${caller_package}::${var}"} = $value; } # end if } # end while } # END import() definition
        Your split doesn't match your data -- the whitespace before the = is optional (zero or more instances) but required after the sign (one or more instances). Change the + to a * and it will probably work better.

        You might put a debugging line after the split, just to see what's in $var and $value: print "\$var is ->$var<-\t\$value is ->$value<-\n"; Besides that, I had things backwards in the original version of my code. Try this instead: $value = $env_var{$1} . $value; That way, it won't go in the wrong place. Oops!

      Shell-style variable interpolation has been discussed in recent nodes. The one you have above won't work at all like you seem to expect.

      PATH = /someplace PATH = $PATH:/somewhere/else
      In this case, $env_var{PATH} would equal ":/somewhere/else/someplace".

      I'm afraid I can't find the original thread, but a fairly robust regular expression looked something like this:

      $value =~ s/$({?)(\w+)(?(1)})/$env_var{$2}/eg;
      This would interpolate stuff like $PATH, ${PATH}, etc.
Re: Parsing variables from login scripts
by qi3ber (Scribe) on Jul 28, 2000 at 18:24 UTC
    Of course, that'll take care of bash, ksh and other shells that use the NAME = VALUE syntax for setting, but csh and it's derivatives use a syntax like (set|setenv) NAME VALUE.

    A major part of this code was taken from cromatic's above, and then twisted in mischevious ways.

    while (<DATA>) { chomp; my ($var, $value); if ( /\s*(\w+)\s*=\s*([^;]+)/ || /\s*(?:set|setenv)\s+(\w+)\s+([^; +]+)/ ) { ($var, $value) = ($1, $2); } else { next; } while ($value =~ /\\$/) { my $temp .= <DATA>; chomp $temp; $temp =~ s/^\s*//; $temp =~ s/\s*$//; $value =~s/\\$/$temp/; } if ($value =~ s/^\$(\w+)//) { $value .= $env_var{$1} || ''; } $env_var{$var} = $value; } foreach my $key (keys %env_var) { print "$key = $env_var{$key}\n"; } __DATA__ NAME1 = VALUE1 set NAME2 = VALUE2 setenv NAME3 = VALUE3 NAME4=VALUE4 \ VALUE5


    I Hope that that helps.
RE: Parsing variables from login scripts
by eLore (Hermit) on Jul 28, 2000 at 20:42 UTC
    sorry... Thank you to all who posted... here's my working code:
    package testpack; # Setup Common Variables sub import { $conf_file = "conf.sh"; open (FILE, $conf_file) || die "Cant open file. \n"; while (<FILE>) { if (! (/^\W/ || /^export/)) { # ignore irrelevants chomp; $var, $value) = split(/=/, $_, 2); if ($value =~ s/^\$(\w+)//) { $value = "$env_var{$1}$value" || ''; } $env_var{$var} = $value; my ($caller_package) = caller; *{"${caller_package}::${var}"} = \$value; } # end if } # end while } # END import() definition 1;
RE: Parsing variables from login scripts
by eLore (Hermit) on Jul 28, 2000 at 21:38 UTC
    Now that I've gotten it partially working, I came upon a possibly better idea. The original thought was to have a perl script parse a .cshrc, .kshrc, or the like for variables. Would the better idea be to source the login/config script in a system call, and pipe the output of a UNIX env command into a hash? That way, all variables would be set exactly as they would occur in a shell. Thoughts?