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

Hello!
I am having trouble executing a system command. Each time that I run it
get an error. Here is the code:
my $hu_seq = $maid_dir."\\".$maid."_hu.fa"; my $hd_seq = $maid_dir."\\".$maid."_hd.fa"; my $contig = $maid_dir."\\".$maid."_fasta.contigs"; open(my $alignment, ">".$maid_dir."\\".$maid."_aln.out"); my $command = 'c:\\blast\bin\blastall -p blastn -d "$hu_seq $hd_seq" - +i $contig -o $alignment'; print $command,"\n"; system($command);
I get the error message "NULL_Caption FATAL ERROR: blast: Unable to open input file $contig"
The path and variable names are correct. I used Data::Dumper to check. Your help will be appreciated!

Replies are listed 'Best First'.
Re: quoting issue with system command
by almut (Canon) on May 12, 2009 at 22:21 UTC

    You probably intended to interpolate those variables (single quotes don't):

    my $command = qq(c:\\blast\\bin\\blastall -p blastn -d "$hu_seq $hd_se +q" -i $contig -o $alignment);

    OTOH, interpolating the file handle $alignment doesn't make much sense... (so what is that -o $alignment supposed to achieve?)

      -o mean print results to an output file. In this case that is $alignment.

        but $alignment is a Perl file handle (that you explicitly opened), and you can't pass Perl file handles to other programs via system() arguments...  Maybe you just want to pass the respective output filename instead (the one you used in the open())?

        ... my $alignment = "$maid_dir\\${maid}_aln.out"; my $command = qq(c:\\blast\\bin\\blastall -p blastn -d "$hu_seq $hd_se +q" -i $contig -o $alignment);
Re: quoting issue with system command
by jwkrahn (Abbot) on May 12, 2009 at 22:32 UTC

    Use a list instead of a string:

    my @command = ( 'c:\\blast\bin\blastall', '-p', 'blastn', '-d', "$hu_s +eq $hd_seq", '-i', $contig, '-o', $alignment ); print "@command\n"; 0 == system @command or die "system @command failed: $?";

      Unfortunately, that doesn't work on Win32. Of course, I believe we should just fix that (though a bit of mushiness on how such things need to be quoted means that the fix wouldn't be foolproof, but I think it is clearly something Perl should try to do). To be specific, system(@list) on Win32 Perl is just the same as system("@list"), unfortunately.

      - tye        

        It works for me.
        my $hu_seq = 'value of $hu_seq'; my $hd_seq = 'value of $hd_seq'; my $contig = 'value of $contig'; my $alignment = 'value of $alignment'; my @command = ( 'echo', '-leprint for @ARGV', '--', '-p', 'blastn', '-d', "$hu_seq $hd_seq", '-i', $contig, '-o', $alignment ); 0 == system @command or die "system @command failed: $?";
        >c:\progs\perl5100\bin\perl t.pl "-leprint for @ARGV" -- -p blastn -d "value of $hu_seq value of $hd_se +q" -i "value of $contig" -o "value of $alignment" >c:\progs\perl589\bin\perl t.pl "-leprint for @ARGV" -- -p blastn -d "value of $hu_seq value of $hd_se +q" -i "value of $contig" -o "value of $alignment" >c:\progs\perl580\bin\perl t.pl "-leprint for @ARGV" -- -p blastn -d "value of $hu_seq value of $hd_se +q" -i "value of $contig" -o "value of $alignment" >c:\progs\perl561\bin\perl t.pl "-leprint for @ARGV" -- -p blastn -d "value of $hu_seq value of $hd_se +q" -i "value of $contig" -o "value of $alignment"

        Here's a version where what you said is true:

        >c:\progs\perl560\bin\perl t.pl -leprint for @ARGV -- -p blastn -d value of $hu_seq value of $hd_seq - +i value of $contig -o value of $alignment

        (Switch echo for $^X for another view.)

        These are all ActivePerl builds.

Re: quoting issue with system command
by John M. Dlugosz (Monsignor) on May 12, 2009 at 23:33 UTC
    In general, you need to learn more about Perl strings and interpolation.

    You put $contig inside of single tick quotes, and that won't interpolate.

    Meanwhile, you wrote ">".$maid_dir."\\".$maid."_aln.out" where you didn't interpolate where a Perl programmer normally would, even though you were using double-tick quotes! For comparison, ">$maid_dir\\${maid}_aln.out" (notice how the last one was handled since it was immediately followed by a character allowed in an identifier).

    Meanwhile, use the 3-argument form of open. Don't concatenate the ">" with the name, but keep it as a separate argument. But, as another poster mentioned, passing the open file, stringified (whatever that does) to the blastall.exe command line doesn't make sense. You meant to pass the file name.

    my $outfile= "$maid_dir\\${maid}_aln.out"; my $command= qq(c:\\blast\\bin\\blastall -p blastn -d "$hu_seq $hd_seq +" -i $contig +-o $alignment);
    Note the use of qq(...) instead of double-tick quotes, so I could use " characters inside the string without escaping them. That's probably why you used single-quotes there in the first place.

    I do like how you composed the command string separately to using it in the system command, so you could easily see what the command was.

    I don't know why you are putting quotes around the two file names together with a space in between, instead of quoting each file name individually. Something about that blastall.exe I suppose, if that's correct. But beware of $maid_dir containing spaces.

    —John

      I have to put quotes around the two filenames with a space in
      between to create the database. $contig is the query. Also, yes
      $maid_dir has can have spaces in it.

      What is the best way to get around that?

      LomSpace
        Put the exe name in quotes, too:
        "D:\Program Files\foo.exe" arg1 arg2 "file_name with spaces.txt"
        The logic of system's argument processing is supposed to make sense on Unix, with underlying OS primitives that work in the same way. But it's a mess on Windows, where the documented meaning is implemented to some degree. Point is, it takes different code paths depending on the form, with different behavior. It's been a while since I looked at it. But if it calls the CMD.exe shell with the whole thing as a string, it behaves the same way as on the command line, as I showed. If it tries to call CreateProcess directly, the exe file name is a separate argument to that.

        I suppose I should refresh my knowledge before writing anything that makes use of that feature myself.