Re: quoting/escaping file names
by AppleFritter (Vicar) on Sep 08, 2014 at 20:41 UTC
|
The shell's most likely your problem here, not Perl. Perl won't look at the contents of $filename if you interpolate it into a string, but the resulting string will be seen by a shell which will happily interpret $var if you're not careful.
The following works for me:
#!/usr/bin/perl
use strict;
use warnings;
my $filename = q{$vaa it's not.txt};
$file =~ s/\$/\\\$/;
my $exec = `touch "$filename"`;
The double quotes (in the `` string) are passed to the shell there, and ensure that the spaces in $filename won't be an issue. The regular expression adds a backslash in front of the dollar sign; this keeps it from being interpreted by the shell. Finally, the single quote in the filename is not considered special by either Perl or the shell.
| [reply] [d/l] |
|
|
It sounds like "$vaa it's not.txt" was only one example of a problematic filename, and so I gotta say I don't think your solution is general enough.
Instead, I can only recommend to avoid the shell entirely (and only using the shell when you really know you really want its features - it really is another layer between Perl and the program you want to call, and you have to know how to use the shell, which isn't a given). This is possible via system (if you read its and the exec docs entirely because the invocation is a little tricky; and the output can be captured by something like Capture::Tiny), IPC::System::Simple, IPC::Run3, and a few others.
| [reply] [d/l] |
|
|
Hello
Here's a pratical example
while(<>) {
my $filename = q{$_};
$filename =~ s/\$/\\\$/;
my $exec = `md5sum "$filename"`;
print $exec ."\n";
}
My input
/mnt1/$Recycle.Bin/S-1-5-21-3093161954-3233498542-1968413288-3198/$I7Q
+M78L.pdf<br>
/mnt1/$Recycle.Bin/S-1-5-21-3093161954-3233498542-1968413288-3198/$ID8
+4PBU.pdf<br>
/mnt1/$Recycle.Bin/S-1-5-21-3093161954-3233498542-1968413288-3198/$IDL
+L4FU.JPG<br>
/mnt1/$Recycle.Bin/S-1-5-21-3093161954-3233498542-1968413288-3198/$IE2
+ZPET.xls<br>
/mnt1/$Recycle.Bin/S-1-5-21-3093161954-3233498542-1968413288-3198/$IF7
+3ZR5.jpg<br>
find /mnt1/\$Recycle.Bin/ -type f | hash.pl
The result ..
md5sum: $_: No such file or directory <br>
md5sum: $_: No such file or directory <br>
md5sum: $_: No such file or directory <br>
md5sum: $_: No such file or directory <br>
But As I said before. file names could have spaces and quotes..
ps: I know there's a DIGEST::MD5. Its only an example
| [reply] [d/l] [select] |
|
|
my $filename = q{$_};
q// (or q{} etc.) is just the single-quote operator; I used that instead of the more customary single quotes to avoid having to escape the single quote in the string itself (which I felt would only serve to complicate the issue).
See Quote and Quote like Operators and Quote Like Operators in perlop. Since this IS a single-quote, BTW, q{$_} (which is exactly the same as '$_') does not interpolate $_ into the string, and you end up passing a literal $_ to md5sum. And of course, it's unlikely that a file with that name exists.
Long story short, just remove that line, it was just there to provide an example filename to work with.
| [reply] [d/l] |
|
|
Why did you type my $filename = q{$_}; what do you think that does?
| [reply] [d/l] |
Re: quoting/escaping file names
by Corion (Patriarch) on Sep 08, 2014 at 19:41 UTC
|
I'm not exactly sure where your problem lies. Can you show us a short example program that demonstrate the problem you encounter, and also where you encounter it?
Maybe the part where you tried the substitutions and how they failed, and/or what you expected to happen?
| [reply] |
|
|
hello.
Take a look at this ..
while(<>) {
chomp;
my $filename = $_;
$filename =~ s/\$/\\\$/g;
$filename =~ s/ /\ /g;
$filename =~ s/\'/\\\'/g;
print $filename ."\n";
my $exec = `md5sum $filename`;
print $exec ."\n";
}
find /tmp/ -type f | ./hash.pl <br>
/tmp/File Wasn\'t found.txt <br>
md5sum: /tmp/File: No such file or directory<br>
md5sum: Wasn't: No such file or directory<br>
md5sum: found.txt: No such file or directory<br>
<br>
Now if I quote $filename
while(<>) {
chomp;
my $filename = $_;
$filename =~ s/\$/\\\$/g;
$filename =~ s/ /\ /g;
$filename =~ s/\'/\\\'/g;
print $filename ."\n";
my $exec = `md5sum '$filename'`;
print $exec ."\n";
}
find /tmp/ -type f | ./hash.pl
/tmp/File Wasn\'t found.txt
sh: Syntax error: Unterminated quoted string
Any Ideias ?
Thanks | [reply] [d/l] [select] |
|
|
| [reply] |
|
|
|
|
|
|
|
use warnings;
use strict;
use IPC::System::Simple 'capturex';
while (my $fn=<DATA>) {
chomp($fn);
$fn=~s/\\n/\n/g; # test filenames with newlines
print "Filename: <<<$fn>>>\n";
# Method 1: capturex
my $m1 = capturex('echo','-n',$fn);
print "capturex: ", $m1 eq $fn ? "PASS" : "FAIL <<<$fn>>>", "\n";
# Method 2: piped open
open my $pipe, '-|', 'echo', '-n', $fn or die $!;
my $m2 = do { local $/; <$pipe> };
close $pipe or die $! ? $! : "\$?=$?";
print "open: ", $m2 eq $fn ? "PASS" : "FAIL <<<$fn>>>", "\n";
}
__DATA__
$vaa it's not.txt
/mnt1/$Recycle.Bin/S-1-5-21-3093161954-3198/$I7QM78L.pdf
/tmp/File Wasn't found.txt
! @ # $ % ^ & * ( - + = { ] | \ : ; " ' < , . / ? ~ `
filename\nwith\newlines
All PASS on my system (Linux). | [reply] [d/l] [select] |
|
|
#!/usr/bin/perl --
##
##
## perltidy -olq -csc -csci=3 -cscl="sub : BEGIN END " -otr -opr -ce
+-nibc -i=4 -pt=0 "-nsak=*"
## perltidy -olq -csc -csci=10 -cscl="sub : BEGIN END if " -otr -opr
+-ce -nibc -i=4 -pt=0 "-nsak=*"
## perltidy -olq -csc -csci=10 -cscl="sub : BEGIN END if while " -otr
+ -opr -ce -nibc -i=4 -pt=0 "-nsak=*"
#!/usr/bin/perl --
use strict;
use warnings;
use Digest;
use Digest::MD5;
use Path::Tiny qw/ path tempfile tempdir cwd /;
use File::Find::Rule qw/ find rule /;
Main( @ARGV );
exit( 0 );
sub Main {
## self test
#~ warn mmd5external( __FILE__ );
#~ warn mmd5perl( __FILE__ );
#~ warn mmd5( __FILE__ );
#~ rule( 'file' )->exec( \&Famatte )->in( '/tmp' );
#~ find( 'file' => 'exec' => \&Famatte , in => [ '/tmp' ] );
rule( 'file' => 'exec' => \&Famatte, )->in( '/tmp' );
} ## end sub Main
sub Famatte {
my( $shortname, $path, $fullname ) = @_;
RealFamatte( $fullname );
return !!0; # discard filename
} ## end sub Famatte
sub RealFamatte {
my( $filename ) = @_;
print mmd5( $filename ), "\t$filename\n";
} ## end sub RealFamatte
sub mmd5 {
goto &mmd5perl;
#~ goto &mmd5external;
} ## end sub mmd5
#~ sub mmd5external {
#~ use File::Which qw/ which /;
#~ use Capture::Tiny qw/ capture /;
#~ my( @args ) = @_;
#~ my $cmd = which('md5sum');
#~ my( $stdout, $stderr, $exit ) = capture {
#~ system( $cmd, @args );
#~ };
#~ if( $exit ){
#~ warn "uhoh got error exit( $exit ) : $stderr ";
#~ } else {
#~ $stdout =~ s/\s+$//; ## "chomp"
#~ return $stdout;
#~ }
#~ }
sub mmd5perl {
my $fh = path( shift )->openr_raw;
my $ctx = Digest->new( 'MD5' );
$ctx->addfile( $fh );
return $ctx->hexdigest;
} ## end sub mmd5perl
perltidy, Digest, Digest::MD5, File::Find::Rule, Path::Tiny, File::Which, Capture::Tiny. Path::Tiny, find/rule | [reply] [d/l] |
Re: quoting/escaping file names
by Anonymous Monk on Sep 08, 2014 at 21:27 UTC
|
The "traditional" *NIX way of doing things like this - using backticks to call the shell and putting together filenames "$like/$this" - starts showing its problems when you start going cross-platform and filenames get more interesting than /var/something/foo_bar (in my experience, "interesting" filenames are more common in Windows than *NIX).
Instead, use something like Path::Class for your filename manipulations, and something like IPC::System::Simple for calling external programs (there are plenty of other similar modules but these should be enough to get started with). In this case, you should be able to do what you want with IPC::System::Simple's capturex('dosomething',$File::Find::name) as a replacement for the backticks. capturex avoids the shell and its interpolations altogether, so funky filenames should work.
| [reply] [d/l] [select] |