2

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.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
claf
  • 9,043
  • 17
  • 62
  • 79
  • 2
    If you just want to get it going quickly, merge the entire line into one string and put backticks around it: i.e. $result = \`dot -Tpng $dottmpfile > $pngfile\`; – PP. Mar 18 '10 at 11:50
  • 1
    see here: http://stackoverflow.com/questions/2461472/how-can-i-run-an-external-command-and-capture-its-output-in-perl/2461664#2461664. Also, to remove files, use `unlink()`. no need to call system `rm` – ghostdog74 Mar 18 '10 at 11:58
  • 2
    You should use $! rather than $? - that will give you the error message rather than the error code, and that will almost always tell you what went wrong. – Kilian Foth Mar 18 '10 at 12:01
  • 1
    Are you getting an error message or just trying to make your code cleaner? – Sam Post Mar 18 '10 at 12:36
  • @PP There is a crucial difference between passing a list versus a single string to `system` (or `qx`). – Sinan Ünür Mar 18 '10 at 12:40
  • @claferri What is your question? – Sinan Ünür Mar 18 '10 at 12:44
  • @Sinan : My question is, what am I doing wrong (as I get quiet a lot of error) – claf Mar 18 '10 at 12:58
  • @Sinan : What is this crucial difference you're talking about ... – claf Mar 18 '10 at 12:59
  • @Sam : I'm getting some errors, but also I'd like to have a clean code. – claf Mar 18 '10 at 12:59
  • I'm looking for the right way to call "dot" command at the end of my script to convert a dot file in png format, then exit. – claf Mar 18 '10 at 13:06
  • @claferri From the docs you linked to: *"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"* – Sinan Ünür Mar 18 '10 at 14:11
  • @Sinan : could you be more specific, I don't get what you're pointing. – claf Mar 18 '10 at 14:21

4 Answers4

3

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";
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
Pavunkumar
  • 5,147
  • 14
  • 43
  • 69
  • @pavun There is a crucial difference between passing a list versus a single string to `system` (or `qx`). – Sinan Ünür Mar 18 '10 at 12:41
  • 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 Mar 18 '10 at 12:49
  • @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? – claf Mar 18 '10 at 13:02
  • @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 Mar 18 '10 at 14:21
  • 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). – claf Mar 18 '10 at 14:26
  • @claferri `system` returns `0` on success. The value of `$!` is meaningless following an operation that succeeded. – Sinan Ünür Mar 18 '10 at 14:34
3

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
  • 116,958
  • 15
  • 196
  • 339
  • 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) – claf Mar 18 '10 at 14:35
  • 1
    @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 Mar 18 '10 at 14:37
  • 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. – claf Mar 18 '10 at 14:38
  • In fact, simply commenting the -- or die "system @args failed : $!"; -- seems to resolv the problem, don't know why ... – claf Mar 18 '10 at 14:40
  • using your code works perfectly too and should be explicit in case an error occurs! – claf Mar 18 '10 at 14:42
  • @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 Mar 18 '10 at 14:44
  • @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 Mar 18 '10 at 19:50
1

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 ... }
mob
  • 117,087
  • 18
  • 149
  • 283
-1

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.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
darkturo
  • 124
  • 3
  • Of course, dot is a well known debian package so there is no way this may be the source of the problem. – claf Mar 18 '10 at 12:57
  • ummmmmm ... I've never heard about that ^_^> ... Thanks for the info – darkturo Mar 18 '10 at 13:48
  • 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 ;) – claf Mar 18 '10 at 13:54