sub execute_command { my ($command,$dummy) = @_; $dummy && return; ($command) = ($command =~ /^\s*(.+)\s*$/); $command || return; return &create_and_read_from_another_process($command,"STDOUT"); } sub backtick { my ($command,$dummy) = @_; $dummy && return; ($command) = ($command =~ /^\s*(.+)\s*$/); $command || return; return &create_and_read_from_another_process($command,"capture_and_return"); } sub create_and_read_from_another_process # Code very similar to get_JGOFS_record stuff (in wjs*perl*routines*) # and should probably be unified. WJS Dec 08 # 25 Dec 12 WJS # Add capability to control where output from executed process ends up # Included adding an entry for each of the 2 (so far) ways of doing things # 2 Dec 12 WJS # Correct documentation comments to reflect that 7 args can be returned # Get output from another pipe to another process, recording all # available diagnostic information. # Returns 0 or 7 values. # 0 vals: Bad input argument list (eg, no command or > 1 arg) # 7 vals: # Val 1 = data received from child process # Val 2 = $? of child process # Val 3 = pid of child process (= return value from open) # Val 4 = $! of open # Val 5 = $! of last read from child process # Val 6 = return value from close # Val 7 = $! of close # Vals 2-7 returned as numbers rather than strings # None of vals 4-7 seem to be documented to mean anything, and, indeed, # using them has often caused trouble in the sense that a normal perl # operation will alter a 0 $! to a non-zero (abnormal) one. Vals are # returned anyway in case my doc reading has come up short # A child that completes properly (not necessarily normally) will # communicate its status either via its $? or what it writes to this # routine. If the child always writes SOMETHING (either an answer or an # error message), then this non-emptiness is the best status available. # The perl book says to check for the successful creation of the child # process by looking to see if the pid is defined (if undefined, this # routine returns the empty string). However, it seems to me that the # child could be created, write to its output, and exit before the pid # check has been performed. The fork system service (presumably used by # perl to create the child) is documented to return 0 in the child process # and, in the parent, either the pid of the child or -1. If the latter, # errno is set to reflect the cause of failure. Thus one could guess that # either the returned pid value or the $! of the open would have failure # info. However, the perl book does not document either. # perl does not document any method of determining a read error # experienced via the angle bracket operator, either. That operator # returns next record or undefined. This routine returns the $! associated # with the undefined angle bracket return. # The perl close function is what causes $? to be set. Presumably the # close could fail, too, but this is not documented to happen. The # $! from close is notoriously flaky (my opinion). Under # normal circumstances on globec (Solaris), for example, it acquires the # value "Illegal seek". In perl's defense, there is no suggestion that # $! is in fact useful after a close. { my ($command,$output_processing_directive,$dummy) = @_; my ($open_result,$record,$command_result,$last_record_status); my ($close_return,$close_status); my ($pid); $dummy && return; ($output_processing_directive eq "STDOUT") || ($output_processing_directive eq "capture_and_return") || return; ($command) = ($command =~ /^\s*(.+)\s*$/); $command || return; $command_result = $close_return = ""; $open_status = $last_record_status = $close_status = 0; $! = $? = 0; $pid = open (BACKTICK,"$command |"); $open_status = $!; defined($pid) || ($pid = ""); if ($pid ne "") { $! = 0; while (defined ($record = )) { if ($output_processing_directive eq "capture_and_return") { $command_result .= $record; } elsif ($output_processing_directive eq "STDOUT") { # Be good to handle errors from next print , but not now (Dec 12) print $record; } else { return; } $! = 0; } $last_record_status = $!; $! = 0; $close_return = close BACKTICK; $close_status = $!; defined ($close_return) || ($close_return = ""); } return $command_result,$?+0,$pid+0,$open_status+0,$last_record_status+0, $close_return+0,$close_status+0; } sub format_backtick_return_status { # Used to format info returned by backtick. # If this routine thinks that everything is normal, it returns the # empty string. Suggested use of this routine is to call it only when # caller believes something has gone wrong. In that case, an empty # string return from this routine represents its own exceptional case # Order of args below matches return from backtick.pl, w/ command # in place of the return of the command. An extra optional arg can be # specified at the end, to be included w/the text w/the prefix "More info:" # backtick users can code as follows # ($results,@status_info) = &backtick($command); # ($results eq "") && # die (&format_backtick_return_status($command,@status_info)); # or # ($results,$exit_status,@status_info) = &backtick($command); # ($exit_status == 0) || # die ( # &format_backtick_return_status # ($command,$exit_status,@status_info) # ); # If $results contains useful diagnostic info, it can be included in the # format call; eg # &format_backtick_return_status($command,@status_info,$results)); # or # &format_backtick_return_status($command,@status_info, # "return from command = $results")); my ($command, $command_exit_status, $open_return,$open_status, $last_io_status, $close_return,$close_status, $extra_info, $dummy ) = @_; my ($bad_call) = " *** terminating error processing due to bad/incomplete " . "call to format_backtick_return_status"; my ($return_val,$initial_return_val); $command || return "*** format_backtick_return_status called w/no args"; $dummy && return "*** format_backtick_return_status called too many args\n" . "*** Call probably dealt with I/O on pipe to $command\n"; $initial_return_val = " *** Problem with I/O on pipe to $command\n"; $return_val = $initial_return_val; (defined $command_exit_status) || return $return_val . $bad_call; ($command_exit_status == 0) || ($return_val .= " *** Abnormal exit status from child process: " . $command_exit_status . "\n"); (defined $open_return) || return $return_val . $bad_call; # open return is documented to be pid, which in parent process is > 0 # (pid of child). perl pids seem to be in decimal, not hex. # Not documented what comes back if open fails - guess is undefined # (arriving here as empty), but could be a -1 (based on fork return # statuses) if ( ! (($open_return =~ /\d+/) && ($open_return > 0)) ) { $return_val .= " *** Abnormal return from pipe open: "; $return_val .= ($open_return eq "") ? "empty/undefined" : $open_return; $return_val .= "\n"; } (defined $open_status) || return $return_val . $bad_call; ($open_status == 0) || ($return_val .= " *** Abnormal status of open close: $open_status\n"); (defined $last_io_status) || return $return_val . $bad_call; ($last_io_status == 0) || ($return_val .= " *** Abnormal final I/O status: $last_io_status\n"); # close return stuff empirically determined (under VMS, no less, so # feel relatively free to alter it) (defined close_return) || return $return_val . $bad_call; if ($close_return != 1) { $return_val .= " *** Abnormal return from pipe close: "; $return_val .= ($close_return eq "") ? "empty/undefined" : $close_return; $return_val .= "\n"; } (defined $close_status) || return $return_val . $bad_call; ($close_status == 0) || ($return_val .= " *** Abnormal status of pipe close: $close_status\n"); (defined $extra_info) && ($extra_info ne "") && ($return_val .= " ... More info (next line et seq)\n$extra_info\n"); ($return_val eq $initial_return_val) && ($return_val = ""); return $return_val; } $BACKTICK_LOADED = 1;