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.

      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.
      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!

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?