double eval?

B

bettyann

i have an input file, Icons.lis, like so:

#--------------
# line starting with "%" is an executable perl command

% $zyxDir = "/System/Library/CoreServices/SystemIcons.bundle/Contents/Resources";
% $cbaDir = "/Users/bettyann/icons/03-08-04_Bubbly-System";

# System Icon Files # Local Icon File
$zyxDir/ApplicationsFolderIcon.icns $cbaDir/older-applications
$zyxDir/CDAudioVolumeIcon.icns $cbaDir/cd
#--------------

how do i get perl to interpret the variable "$zyxDir" and "$cbaDir" in
the filenames (in the last two lines of the above input file)? i can
evaluate the "%" command just fine but i cannot resolve/ interpret/
expand the filenames. in other words, how do i get the first
filename, $zyxDir/ApplicationsFolderIcon.icns, to resolve to
/System/Library/CoreServices/SystemIcons.bundle/Contents/Resources/ApplicationsFolderIcon.icns?

my code:

#!/usr/bin/perl
# open the list file
$listFile = "Icons.lis";
open( FID, "< $listFile" ) || die "Could not open $listFile for
reading.";
while(<FID>){ # Read line by line
# skip blanks and comment lines
s/#.*//; # ignore comments by erasing them
next if /^(\s)*$/; # skip blank lines
chomp; # remove trailing newline characters
if ( s/^%// ) {
# this is an executable perl command
print "eval >>$_<< \n";
eval( $_ ); # debug
next;
}
# debug - these variables are defined from above "eval"
print "\n";
print "zyxDir >>$zyxDir<< \n";
print "cbaDir >>$cbaDir<< \n";
# get the two filenames from this line
( $sysIcns, $newIcon ) = split;
print "sysIcns >>$sysIcns<< \n";
print "newIcon >>$newIcon<< \n";
`ls $sysIcns`;
} # while( <FID>)
close( FID );

i get this output and error:

eval >> $zyxDir = "/System/Library/CoreServices/SystemIcons.bundle/Contents/Resources";<<
eval >> $cbaDir = "/Users/bettyann/icons/03-08-04_Bubbly-System";<<

zyxDir >>/System/Library/CoreServices/SystemIcons.bundle/Contents/Resources<<
cbaDir >>/Users/bettyann/icons/03-08-04_Bubbly-System<<
sysIcns >>$zyxDir/ApplicationsFolderIcon.icns<<
newIcon >>$cbaDir/folder-applications<<
ls: /ApplicationsFolderIcon.icns: No such file or directory

the error is presumably because the "$zyxDir" in $sysIcns is not
evaluated. i tried a number of eval combinations ... with no success.

thanks - bettyann
 
G

Gunnar Hjalmarsson

bettyann said:
i have an input file, Icons.lis, like so:

#--------------
# line starting with "%" is an executable perl command

% $zyxDir = "/System/Library/CoreServices/SystemIcons.bundle/Contents/Resources";
% $cbaDir = "/Users/bettyann/icons/03-08-04_Bubbly-System";

# System Icon Files # Local Icon File
$zyxDir/ApplicationsFolderIcon.icns $cbaDir/older-applications
$zyxDir/CDAudioVolumeIcon.icns $cbaDir/cd
#--------------

how do i get perl to interpret the variable "$zyxDir" and "$cbaDir"
in the filenames (in the last two lines of the above input file)?

You are asking a FAQ.

perldoc -q "expand variables"

You can follow one of the suggestions in the FAQ answer. However, a
more secure method is to do plain substitutions:

$sysIcns =~ s/\$zyxDir/$zyxDir/;

my code:

#!/usr/bin/perl

Where are

use strict;
use warnings;

??
 
B

Bob Walton

bettyann said:
i have an input file, Icons.lis, like so:

#--------------
# line starting with "%" is an executable perl command

% $zyxDir = "/System/Library/CoreServices/SystemIcons.bundle/Contents/Resources";
% $cbaDir = "/Users/bettyann/icons/03-08-04_Bubbly-System";

# System Icon Files # Local Icon File
$zyxDir/ApplicationsFolderIcon.icns $cbaDir/older-applications
$zyxDir/CDAudioVolumeIcon.icns $cbaDir/cd
#--------------

how do i get perl to interpret the variable "$zyxDir" and "$cbaDir" in
the filenames (in the last two lines of the above input file)? i can
evaluate the "%" command just fine but i cannot resolve/ interpret/
expand the filenames. in other words, how do i get the first
filename, $zyxDir/ApplicationsFolderIcon.icns, to resolve to
/System/Library/CoreServices/SystemIcons.bundle/Contents/Resources/ApplicationsFolderIcon.icns?

....


thanks - bettyann

Well, besides being a FAQ (see perldoc -q variables), what you are doing
is generally a *bad idea*. You are assigning to variable names you are
reading from an input file. If your input file is totally under your
control and no one else ever prepares any of it, you might argue that
you're OK, but in general this is a huge no-no. Consider that *any*
variable name could be specified -- consider what could happen if
someone put the variable $sysIcns in the input. Also, you must consider
the effects of input like:

% system('rm -f /');

to your scheme. In addition, consider what the backticks could do with
a filename given like:

% $xxx = "Some/nice/path;rm -f /;";

You should never execute a command on your system unless you have
verified it doesn't contain stuff like that.

So what should you do?

1. In this case, you could simply parse the "%" lines and put the
result in a hash. That way, no matter what kind of crap they contain,
they won't trash your system or pollute your namespace. Something like:

s/%\s+\$(\w+)\s*=\s*"(.*?)"\s*;/$hash{$1}=$2/e;

2. Then when you want to "interpolate" the "variable" into a "string",
you could use something like:

$sysIcns=~s/\$(\w+)/$hash{$1}/g;

3. Validate the string passed to the ls command in the backticks
operator so you know it won't do anything nasty.

Here is a complete example which may be copy/paste/executed:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %hash;
my $sysIcns;
my $newIcon;
while(<DATA>){ # Read line by line
# skip blanks and comment lines
s/#.*//; # ignore comments by erasing them
next if /^(\s)*$/; # skip blank lines
chomp; # remove trailing newline characters
if ( s/^%// ) {
# this is an executable perl command
print "eval >>$_<< \n";
#eval( $_ ); # debug
s/\s*\$(\w+)\s*=\s*"(.*?)"\s*;/$hash{$1}=$2/e;
next;
}
# debug - these variables are defined from above "eval"
print "\n";
print Dumper(\%hash);
# print "zyxDir >>$zyxDir<< \n";
# print "cbaDir >>$cbaDir<< \n";
# get the two filenames from this line
( $sysIcns, $newIcon ) = split;
#$sysIcns=eval "\"$sysIcns\""; <-- (what you would have needed)
#$newIcon=eval "\"$newIcon\"";
$sysIcns=~s/\$(\w+)/$hash{$1}/g;
$newIcon=~s/\$(\w+)/$hash{$1}/g;
print "sysIcns >>$sysIcns<< \n";
print "newIcon >>$newIcon<< \n";
`ls $sysIcns` if $sysIcns=~m!^[/\w.-]+$!; #validate
} # while( <FID>)
#close( FID );
close( DATA );
__END__
#--------------
# line starting with "%" is an executable perl command

#stuff removed from next line so it doesn't wrap
% $zyxDir = "/System/SystemIcons.bundle/Contents/Resources";
% $cbaDir = "/Users/bettyann/icons/03-08-04_Bubbly-System";

# System Icon Files # Local Icon File
$zyxDir/ApplicationsFolderIcon.icns $cbaDir/older-applications
$zyxDir/CDAudioVolumeIcon.icns $cbaDir/cd
#--------------
 
J

Joe Smith

bettyann said:
eval >> $cbaDir = "/Users/bettyann/icons/03-08-04_Bubbly-System";<<

If the contents of $cbaDIR was "FOOBAR" before the eval, then the
eval would be interpreted as
FOOBAR = "/Users/bettyann/icons/03-08-04_Bubbly-System";
because there was no backslash in front of the $ in $cbaDIR.

Take the advice of the other posters in this thread, or look at
http://search.cpan.org/search?query=config+file for better ways.
-Joe
 
G

Gunnar Hjalmarsson

Joe said:
If the contents of $cbaDIR was "FOOBAR" before the eval, then the
eval would be interpreted as
FOOBAR = "/Users/bettyann/icons/03-08-04_Bubbly-System";
because there was no backslash in front of the $ in $cbaDIR.

No, it wouldn't. The above is a result of

print "eval >>$_<< \n";

in the while loop, and the variabel name in the $_ string is not
interpolated.
Take the advice of the other posters in this thread, or look at
http://search.cpan.org/search?query=config+file for better ways.

Concur.
 
B

bettyann

thanks bob + all,
You are asking a FAQ.

perldoc -q "expand variables"

this is the frustrating thing for me ... i didn't know what to search
for in perldoc. it's hard to know what you don't know.
what you are doing is generally a *bad idea*.

yes, thank you for pointing that out. this little script is for my
own "personal" use -- just for housekeeping. but, in the interest of
learning new things, i am taking your advice and will not implement
this *bad idea* ;)
1. In this case, you could simply parse the "%" lines and put the
result in a hash. That way, no matter what kind of crap they contain,
they won't trash your system or pollute your namespace. Something like:

s/%\s+\$(\w+)\s*=\s*"(.*?)"\s*;/$hash{$1}=$2/e;

nice. if i understand correctly, i'm building my own "dictionary"
where $1 is the variable name and $2 is its 'definition'.
2. Then when you want to "interpolate" the "variable" into a "string",
you could use something like:

$sysIcns=~s/\$(\w+)/$hash{$1}/g;

and then i use my little ol' "dictionary" to substitute.
3. Validate the string passed to the ls command in the backticks
operator so you know it won't do anything nasty.

this is the part i don't understand well. i'm trying to understand
the m// command:
`ls $sysIcns` if $sysIcns=~m!^[/\w.-]+$!; #validate

(i feel as if i am back in my high school french class where i can
figure out a few individual words but cannot really get the grammar
and hence the big-picture.) i think the m// command is saying:

match beginning of line
all the things in this class [...]
the class is made up of: forward-slash, word, any-char, dash
match this class one-or-more times (i thought just one-time, {1}, made
sense)
match end of line

i don't understand how this 'validates' a safe command/string.
while(<DATA>){ # Read line by line [snip]
} # while( <DATA>)
close( DATA );
__END__
#--------------
# line starting with "%" is an executable perl command

#stuff removed from next line so it doesn't wrap
% $zyxDir = "/System/SystemIcons.bundle/Contents/Resources";
% $cbaDir = "/Users/bettyann/icons/03-08-04_Bubbly-System";

# System Icon Files # Local Icon File
$zyxDir/ApplicationsFolderIcon.icns $cbaDir/older-applications
$zyxDir/CDAudioVolumeIcon.icns $cbaDir/cd
#--------------

this is cool!

thanks, i got this script going. i look forward to understanding the
m// expression better, tho.
 
G

Gunnar Hjalmarsson

bettyann said:
this is the frustrating thing for me ... i didn't know what to
search for in perldoc. it's hard to know what you don't know.

Yep. That's why I pointed you to it.

OTOH, you are expected to make a try before posting a question here.
And, actually, it's not that difficult. Personally I like to browse
the Perl docs. One place where the FAQ is available in HTML format is
http://www.perldoc.com/perl5.8.4/pod/perlfaq.html
 
B

Bob Walton

bettyann said:
thanks bob + all, ....
`ls $sysIcns` if $sysIcns=~m!^[/\w.-]+$!; #validate

(i feel as if i am back in my high school french class where i can
figure out a few individual words but cannot really get the grammar
and hence the big-picture.) i think the m// command is saying:

match beginning of line
all the things in this class [...]
the class is made up of: forward-slash, word, any-char, dash

-------------------------------------------------^^^^^^^^
In a character class, . stands for itself, as does almost every other
character other than \ and those escaped by it, and - not at the
beginning or end, and ... well, read up on the rules for character
classes in "perldoc perlre". It's a bit complicated. Anyway, the
repeated character class will match any string containing only
characters among letters, digits, underscore, period, hyphen, and
forward-slash. It will fail to match any string containing any other
character, including one containing a ; . Also, the character class
will not match whitespace, which also disallows "rm -f /" or any other
shell command requiring whitespace.

match this class one-or-more times (i thought just one-time, {1}, made
sense)


No, we want to match *every* character in the string. That's why the ^
to force the match to start at the start of the string, the repeated
character class, and the $ to force the match to end at the end of the
string. Then, if there is any character in the string which is not
matched by the character class, the match will fail. m!...! was used to
avoid possible issues quoting the / default regexp delimiter (I can't
remember if a delimiter has to be quoted inside a character class or
not, and I'm to lazy to look it up).

match end of line

i don't understand how this 'validates' a safe command/string.


Well, in the specific example given ("blah/blah/blah;rm -f /;"), it will
not match because the string being given to ls contains a semicolon
(typically the shell's character to separate multiple commands on a
single line). Without a ; everything will be an argument to ls, and not
much harm can come from that. It also will fail to match the whitespace
in the "rm -f /" command. The principle which was used in constructing
the validation expression was to specifically include all characters
which are desired to be permitted, and to exclude all others. This is
better than trying to specifically exclude those you don't want --
you're likely to forget one or more, leading to an insecure script. For
example, what about "blah/blah|rm -f /"? Are there more? Maybe? The
character class listed will match all your examples -- depending on the
file names, though, something else may be required. An even better idea
would be to use something else to list your files besides a system
command, perhaps the stat() function. That way, you can't inadvertently
pass bad stuff to a shell, since there isn't any shell involved.

HTH.

....
 
K

krakle

Gunnar Hjalmarsson said:
You are asking a FAQ.

perldoc -q "expand variables"

You can follow one of the suggestions in the FAQ answer. However, a
more secure method is to do plain substitutions:

$sysIcns =~ s/\$zyxDir/$zyxDir/;



Where are

use strict;
use warnings;

??

and -Tw
 
B

Ben Morrow

Quoth (e-mail address removed) (bettyann):
3. Validate the string passed to the ls command in the backticks
operator so you know it won't do anything nasty.

this is the part i don't understand well. i'm trying to understand
the m// command:
`ls $sysIcns` if $sysIcns=~m!^[/\w.-]+$!; #validate

(i feel as if i am back in my high school french class where i can
figure out a few individual words but cannot really get the grammar
and hence the big-picture.) i think the m// command is saying:

match beginning of line
all the things in this class [...]
the class is made up of: forward-slash, word, any-char, dash
match this class one-or-more times (i thought just one-time, {1}, made
sense)
match end of line

i don't understand how this 'validates' a safe command/string.

The point here is that the ^ and the $ make sure that the string doesn't
contain anything *but* /\w.-: for instance, it isn't '; rm -rf ~'.

Note that there is no need to use ls here at all: it would be faster to
use opendir/readdir.

Ben
 
B

bettyann

You are asking a FAQ.
Yep. That's why I pointed you to it.

yes, thank you.
OTOH, you are expected to make a try before posting a question here.
And, actually, it's not that difficult. Personally I like to browse
the Perl docs. One place where the FAQ is available in HTML format is
http://www.perldoc.com/perl5.8.4/pod/perlfaq.html

actually, gunnar, i did make the effort. and a rather big effort,
too. as i say, it is hard to know what you don't know. i kept
searching for things like "eval" which is a tcl command/concept. as
you can guess, searching "substitute" comes up with all sorts of stuff
.... not relevant to what i was looking for.

thanks for your help. i appreciate it.
 
B

bettyann

bob + ben,

thanks. now that i understand *what* we're matching (basically, a
filename that cannot contain [space], et al), i better understand
*how* you did it.

thanks for all your help. i do appreciate your efforts.
- bettyann
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top