in reply to Using perl to call a shell command?

If you decide to use system rather than backticks, you might want to set $| = 1 to avoid buffering problems.

Also bear in mind that any shell command you run will be running under the web server's user ID and it's environment will be different to when you run the same command from a shell prompt.

Replies are listed 'Best First'.
Re: Re: Using perl to call a shell command?
by MarkM (Curate) on Mar 08, 2003 at 04:44 UTC

    How is setting $| = 1 going to avoid buffering problems when calling system() on a non-Perl program?

    I believe you are referring to fork(), and exec(), not system(). I also believe that modern versions of Perl include special code in fork() and exec() to implicitly flush all open file handles.

    UPDATE: After discussing with grantm, the problem that he was dealing with in the past was a parent process expecting output from the perl script and a child of the perl script in the proper order, such as would be the case for a CGI that wishes to insert the output of a shell command directly into the HTML at a certain point. In this case, in order for the parent to see the output in the correct order, the output must be flushed before the shell command is executed. At least Perl 5.8.0 does this automatically for all systems that support the 'flush all handles' semantics (often fflush(NULL)). I recommend not using auto-flushing for CGI handles as it unnecessarily breaks the writes up into separate system calls, leading to inefficient web page serving. Flush when necessary, and let Perl autoflush when necessary.

      Wow! I am lost already. What I am after most specifically is to have a web page with an entry blank for a specified IP address entry. When the user submits the request, the page instructs the system to run a command with the IP address inserted into the command on the system. This command can be assigned the proper UID credentials and is also something I would normally run from the command line as a regular user. I will be calling nmap from the web page. Your lofty assistance is appreciated.

        You might have already thought about this but; if you are accepting input from a webpage and using that to run a shell command, you should make sure you have sanitized the input and removed any nasties that could be potentially be sent in. There is more infomration at Cross Scripting Vulnerability and in perlsec.

        Also, if you are using system, make sure you use pass in the arguments to the shell commnad as a separate list (second form of calling system) rather than building up a string with the command and argument and handing that to system.

        Cheers, data64


        Just a tongue-tied, twisted, earth-bound misfit. -- Pink Floyd

        If you plan on inserting the output of the command directly into your HTML, just use system(). If the command has output and you do not want the output to be inserted into the HTML, you are going to have to redirect or intercept the output.

        The simplest way to redirect the output on UNIX using system() is to append ">/dev/null" (to throw away stdout) or ">/path/to/logfile" (to keep stdout in a logfile). On WIN32 the same can be accomplishing using ">NUL" and ">c:\\path\\to\\logfile". For example:

        system("echo hello >/dev/null");

        To intercept the output use ``:

        my $output = `echo hello`;
      I believe you are referring to fork(), and exec(), not system()

      So if you don't think system() does a fork() and an exec(), what do you think it does?

      Update: Sorry MarkM I hadn't taken the time to understand your point properly. My experience of 500 server errors resulting from no autoflush dates back possibly as far as 5.001 and may have only been under IIS 2.0 on Windows - it's a habit I've carried for a while and can probably drop. The conclusion: Don't mess unnecessarily with $| in your CGI scripts - it won't improve efficiency.

        The difference is that at one point in time, Perl fork() (different from C fork()) did not flush the handles, and any output sent after fork() may be flushed from both parent and child resulting in double output. system() never had this behaviour because system() was not implemented in terms of Perl fork() and Perl exec().

        My real question to you is: What sort of buffering problem do you see $| = 1 solving for system() calls? I say none, and suspect you are referring to the earlier problems with Perl fork(). What say you?