tags:

views:

195

answers:

4

I'm a beginner in Perl and I have some trouble using the "system" call. Here is a little piece of code where I try to execute 2 shell commands :

# First command is :
# dot -Tpng $dottmpfile > $pngfile
# Second command is :
# rm $dottmpfile

if (!($pngfile eq "")) {
  my @args = ("dot", "-Tpng", $dottmpfile, " > ", $pngfile);
  system (join (' ' , @args ))
    or die "system @args failed : $!";

  unlink $dottmpfile;
}

EDIT : Here is my code now, and I still get an error :

system dot -Tpng toto.dot  >  toto.png failed : Inappropriate ioctl for device at /home/claferri/bin/fractal.pl line 79.

I've used system to produce this piece of code.

+3  A: 

You are using > to tell the shell to redirect output to a file yet by using invoking system LIST, you are bypassing the shell. Therefore, you can use:

system ( join (' ' , @args ) ); 

or

system "@args";
pavun_cool
@pavun There is a crucial difference between passing a list versus a single string to `system` (or `qx`).
Sinan Ünür
There is, and the fact that it's a list and not a string is what prevents claferri's code from working, since it relies on shell features.
hobbs
@hobbs : so if I understand what you're saying, the problem is that there are no spaces in the list and the join function make a string with space separated element from a list?
claferri
@hobbs I missed the `>`. Thank you for the hint. @pavun Edited your answer to add explanation and convert my downvote to an upvote. Apologies for the misunderstanding.
Sinan Ünür
This seems to be almost right, as the command does produice the png output file, but I get "failed : Inappropriate ioctl for device at /home/claferri/bin/fractal.pl line 79." and my program exit before reaching the end of the script (unkink $dottmpfile).
claferri
@claferri `system` returns `0` on success. The value of `$!` is meaningless following an operation that succeeded.
Sinan Ünür
A: 

Is the "dot" executable in the PATH? Does it have executable permissions? Which specific error are you getting with this code?

It seems that is correct according to perldoc -f system.

darkturo
Of course, dot is a well known debian package so there is no way this may be the source of the problem.
claferri
ummmmmm ... I've never heard about that ^_^> ... Thanks for the info
darkturo
@darkturo : it's a graph tool (package : graphviz) which is use to create graph image (png file for example) from text file describing a graph, very usefull for me ;)
claferri
@claferri I will take a look on it ;-) ...
darkturo
+3  A: 

Looking at perldoc -f system, note:

If there is more than one argument in LIST, or if LIST is an array with more than one value, starts the program given by the first element of the list with arguments given by the rest of the list. If there is only one scalar argument, the argument is checked for shell metacharacters, and if there are any, the entire argument is passed to the system's command shell for parsing

You are invoking system LIST so the > ends up being passed to dot instead of being interpreted by the shell.

I would recommend that you keep using system LIST because it is a little safer than passing everything through the shell. According to the docs, you can specify the output file by using the -o option to dot, so do that.

If you really want to dot your is and cross your ts (pun not intended), then you can use:

if ( defined $pngfile and $pngfile ne '') {
    my @args = (dot => '-Tpng', $dottmpfile, "-o$pngfile");
    if ( system @args ) {
        warn "'system @args' failed\n";
        my $reason = $?;
        if ( $reason == -1 ) {
            die "Failed to execute: $!";
        }
        elsif ( $reason & 0x7f ) {
            die sprintf(
                'child died with signal %d, %s coredump',
                ($reason & 0x7f),  
                ($reason & 0x80) ? 'with' : 'without'
            );
        }
        else {
            die sprintf('child exited with value %d', $reason >> 8);
        }
    }
    warn "'system @args' executed successfully\n";
    unlink $dottmpfile;
}
Sinan Ünür
I don't get what you're recommending to me? Or did you want to say "wouldn't" ? Then what else should I use? My need is just to process the dot command, and get it's output in the $pngfile (using '>' was my first reflex because it's the way I do it in bash, but there may be better ways to do it in perl I guess)
claferri
@claferri I am recommending that you keep the `system LIST` form and specify the output file using the `-o` option rather than the shell redirection operator `>`.
Sinan Ünür
That seems to be much more clear now :) thx!
claferri
I still get the "failed : Inappropriate ioctl for device at /home/claferri/bin/fractal.pl line 79." I think it may come from something else ... I'm having a look at the previous part of the code.
claferri
In fact, simply commenting the -- or die "system @args failed : $!"; -- seems to resolv the problem, don't know why ...
claferri
using your code works perfectly too and should be explicit in case an error occurs!
claferri
@claferri As I pointed out before, `system` returns `0` if it **succeeds**. Therefore, if you use `system(...) or die` your code will die when `system` succeeds. The value of `$!` will be meaningless in that case. That's why the docs show `system(...) == 0 or die`.
Sinan Ünür
Ok, now I get everything :) thx for the help!
claferri
@claferri: It's in tricky situations like this where I use IPC::System::Simple's version of `system()` -- it takes care of all the little fiddly edge cases.
Ether
+1  A: 

system returns 0 on success and non-zero on "failure". It's contrary to the way most of these idioms look and a little counter-intuitive, but with system calls you should use an expression like:

system($command) and warn "system $command: failed $?\n";   # and not or

or

if (system($command) != 0) { ... handle error ... }
mobrule