in reply to Passing commands to subroutines
The problem will not only be spaces. Anything that is special to the shell will be a problem. To avoid such problems, don't use 1-arg system. Use multiple arg evocation.
See the system manual page, and the perlipc document for details.
Re^2: Passing commands to subroutines
by BioLion (Curate) on Jul 01, 2009 at 16:11 UTC
|
This site has a pretty good discussion of these sorts of problems, and helped me in a similar situation. In particular this fix worked well for me and seemed fine when moving between Unix and windows (i didn't try anything else)... "Spaces could be transparently handled (no pun intended) with U+00A0, a non-breaking space, which in fact it is. Really. If the system is presented with a filename containing U+0020, it just replaces it unilaterally with U+00A0." Hope this helps
UPDATE: This clearly isn't how i got round the problem too, as it doesn't get around the original problem (see example below)... Thanks JavaFan
#!/usr/bin/perl
use strict;
use warnings;
use encoding 'utf8';
my $dir = "/tmp/Foo";
unless (-e $dir){
mkdir $dir or die;
}
chdir $dir or die;
opendir my $dh, $dir or die;
my @files = readdir $dh;
closedir $dh;
print "Got ", scalar @files, " files\n";
foreach my $char ("\x{20}", "\x{A0}") {
my $file = "foo{$char}bar";
open my $fh, ">", $file or die;
}
opendir $dh, $dir or die;
@files = readdir $dh;
closedir $dh;
print "Got ", scalar @files, " files\n";
open my $fh, ">", 'foo baz' || die "Failed to open file : $!";
close $fh || die "Failed to close file : $!";
foreach my $char ("\x{20}", "\x{A0}") {
my $file = "foo".$char."baz";
if (-e $file){
print "got it\n"
} else {
print "not got it...\n";
}
}
system("cat foo\x{20}baz");
system("cat foo\x{A0}baz");
Just a something something...
| [reply] [d/l] |
|
Replacing characters in file names is just plain wrong in Unix. A space is a space, and not something else. And something that isn't a space, just isn't.
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
my $dir = "/tmp/Foo";
mkdir $dir or die;
chdir $dir or die;
opendir my $dh, $dir or die;
my @files = readdir $dh;
closedir $dh;
say "Got ", scalar @files, " files";
foreach my $char ("\x{20}", "\x{A0}") {
my $file = "foo{$char}bar";
open my $fh, ">", $file or die;
}
opendir $dh, $dir or die;
@files = readdir $dh;
closedir $dh;
say "Got ", scalar @files, " files";
__END__
Got 2 files
Got 4 files
See, two different files - one with a space, the other with a non-breaking space. No automatic conversion between them. | [reply] [d/l] |
Re^2: Passing commands to subroutines
by citromatik (Curate) on Jul 01, 2009 at 17:11 UTC
|
Hmmm... the original code redirects the system call:
executeComm ("program $ref_file > $outfile");
is there a way to use redirection in a system call using the multiple-arg version? this doesn't work: system ($program, $ref_file, ">",$outfile)
| [reply] [d/l] [select] |
|
Your whole reason to use the multiple argument version of system was to avoid having the your arguments treated as anything but literal text.
And now you're asking why it doesn't work when the code does exactly that.
If you want redirection, you'll either have to build a shell command or do it yourself (IPC::Run, IPC::Run3, IPC::Open2, IPC::Open3, etc.)
| [reply] [d/l] |
|
You'd use fork and exec. Did you read the perlipc manual page I suggested? It contains code that does what you want which you can copy and adjust. I'm not going to cut and paste it for you.
| [reply] |
|
Given that you're facing a problem with tricky file names, the easiest substitute I could imagine for fixing a line of code like this:
system("program $ref_file > $outfile");
would be to do it like this:
open( PROG, "-|", "program", $ref_file ) or die "can't launch 'program
+' on $ref_file: $!\n"
open( OUT, ">", $outfile );
while (<PROG>) {
print OUT;
}
close PROG;
close OUT;
That ought to take any sort of goofy file name safely in stride (for both input and output files). | [reply] [d/l] [select] |
|
Probably easier to use one of the IPC:: modules, maybe IPC::Cmd
| [reply] |
|
Multiple-arg system() doesn't quite work like that (read this). Redirection using the shell metacharacter ">" is handled by the shell, which reads the entire string "program $ref_file > $outfile", parses it, and executes the command with redirection.
When you do multiple-arg system, you're going raw and skipping the shell (usually). Redirections have to be performed manually and you'll lose some convenience you get with using a shell. Example:
sub executeComm {
my ($outfile, @comm) = @_;
print "cmd: <", join("> <" => @comm), ">\n";
# manual redirection - dup(2) STDOUT first
open(my $ORIGSTDOUT, ">&" . fileno(*STDOUT)) or die $!;
open(*STDOUT, ">", $outfile) or die $!;
# run it!
my $exit = system @comm;
# restore STDOUT
open(*STDOUT, ">&=" . fileno($ORIGSTDOUT)) or die $!;
print "exit: ", $exit, "\n";
}
print "before\n";
executeComm($outfile, $program, $ref_file);
print "after\n";
If you really want a quick-and-dirty fix for your "whitespace-in-filename" problem, place single-quotes around the filenames, as in:
executeComm ("program '$ref_file' > '$outfile'");
Now make sure you don't have single-quotes in the filenames... | [reply] [d/l] [select] |
|
Have you read 'perldoc -f system'?
system "foo >bar";
invokes the shell, and it is the shell which does redirection (> <) | [reply] [d/l] |
|
| [reply] [d/l] |
|
|