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

I require some assistance. I am writing a CGI script in Perl on Solaris that needs to run external commands using the 'system' operator. These commands are part of a larger package that needs the system initialized (via a command) properly before execution, or else each individual command cannot interact with the other, like the following.

1: system "/PATH/initialize_system"; # init the environment
2: system "/PATH/skew.k ARG_FOR_FRAME_NUMBER"; # create image
3: system "/PATH/savegif.k FRAME_NUMBER"; # save image off
4: system "exit"; # exit the environment created with initialize_system

When running a command using 'system', the environment appears to only lasts until the next line of Perl code, so line 2 can't talk to line 3 because the environment required for them to communicate isn't there which is initialized by line 1. The above code snippet would the way it is run from a command line. Any help would be appreciated. Thanks.

Replies are listed 'Best First'.
RE: Environmental Settings
by Adam (Vicar) on Apr 28, 2000 at 02:15 UTC
    When you shell out of Perl, the environment is set using the %ENV hash. You can alter this hash within your script, and then each shell call will use your altered %ENV. When the script ends, the environment will revert to whatever it was before your script. (unless you edit it some other way.)
    So instead of calling your init routine, write it into your perl script.
      Well noted! Same as CGI... Those who use the Oracle DBD driver have found that it won't fly unless you set the Oracle ENVs correctly. A common example of this is printing out the %ENV hash on test.pl's:
      print "Content-type: text/plain\n\n"; foreach (keys %ENV) { print "\$ENV{$_}\t$ENV{$_}\n"; }
      (some of these however should be respected as read-only...)

      cheers!
      I think the problem is that system will create a sub-shell therefore any environment variables defined will be out of scope to the calling process, what you really want is a perl version of the C function 'putenv'(btrott and chromatic will make me a liar by saying one exists already :-) Adding to the ENV hash seems the best way to go.
Re: Environmental Settings
by ZZamboni (Curate) on Apr 28, 2000 at 01:16 UTC
    I imagine that the initialization step sets environment variables or otherwise alters the environment for the next commands. However, the environment only lasts for the current shell, and each system command opens a different shell. Therefore, any alterations to the environment made by the first command will not be seen by the second. You could try putting all the commands in a single system:
    system("/PATH/initialize_system; /PATH/skew.k ARG_FOR_FRAME_NUMBER;". "/PATH/savegif.k FRAME_NUMBER; exit");
    That way they all get executed by the same shell. Don't forget to check the return code of system. See its documentation for the proper way of doing it.

    On second thought, it looks as if the initialize_system command starts a new shell, within which you execute the other commands, right? If this is the case, the above will not work. You could try doing the following if initialize_system accepts commands from stdin:

    system('echo "/PATH/skew.k ARG_FOR_FRAME_NUMBER;/PATH/savegif.k"|'. '/PATH/initialize_system');
Re: Environmental Settings
by turnstep (Parson) on Apr 28, 2000 at 01:19 UTC
    You could attach all the commands together and send them in one big command, separated by semicolons:
    $command = "/PATH/initialize system; /PATH/skew.k blahblah; more comma +nds; exit"; system($command);
      Probably best to use && instead, as: system("command1 && command2 && command3") so that if command1 fails, command2 and 3 won't excecute. This is useful if further commands depend on previous commands (or could have bad consequences if they aren't executed).

      A better way would be to write a shell script that takes 2 arguments and then call that from perl. Ie:

      script.sh #!/bin/bash /PATH/initialize_system
      /PATH/skew.k $1
      /PATH/savegif.k $2

      And then your perl script just runs:

      system("script.sh $arg_for_frame_number $frame_number");

      I guess the shell script could be perl too, but there is more than one way to do it :)

Re: Environmental Settings
by providencia (Pilgrim) on Apr 28, 2000 at 18:25 UTC
    I don't use system() when it's possible not to.
    I've been using open(FH,"some_system_command any options |")
    Then I just: $variable = <FH>.
    I was told that using system() is costly because it forks another process and
    isn't preferred with CGI. I'm a newbie and haven't done any CGI yet.
    That's what I'm getting ready to do now.
    Also there are some functions like chmod() and unlink() to name two
    that do what some of the unix system commands do.
    That's the least costly option.
      Right, calling system() or `` does fork another process along with all the respective overhead. But I'm not sure that your method doesn't do this too. I've never seen that idea before. The way to check is to use perl to do a system call of a program that prints the current process list and then seeing which methods create an extra process to do this. I'd wager that they all do.
Re: Environmental Settings
by Anonymous Monk on Apr 28, 2000 at 17:30 UTC
    You could also use "here-is" input for system command:
    system <<'END_OF_FILE' ;
    /PATH/initialize_system # init the environment 
    /PATH/skew.k ARG_FOR_FRAME_NUMBER 
    /PATH/savegif.k FRAME_NUMBER
    END_OF_FILE