skip to Main Content

Suppose I have the following PHP script being run on the PHP CLI in a shell script:

<?php
# foo.php

$a = rand(1,5);

if ($a == 1) {
    fwrite(STDOUT, 'normal condition');
} else {
    fwrite(STDERR, 'error condition');
}

The script is run in the shell like so:

php foo.php

How do I handle the output from the PHP script in the shell based on whether the output is to STDERR or STDOUT? For instance, if the output from the PHP script is STDOUT, write it to a CSV file, but if the output is to STDERR, write the output to an error log file.

2

Answers


  1. You redirect each stream to separate locations.

    php file.php > outfile.csv 2> errorlog.txt

    See https://askubuntu.com/questions/625224/how-to-redirect-stderr-to-a-file for more details

    Login or Signup to reply.
  2. Some possibilities that produce a single output file.


    Option 1 – redirect stdout and stderr to separate files, then remove empty files:

    php file.php > ./data.csv 2> ./error.txt
    [[ ! -s ./data.csv ]] && rm ./data.csv
    [[ ! -s ./error.txt ]] && rm ./error.txt
    

    The ! -s test will be true if a file is zero-length. This version uses some fairly common shell idioms, and should work in both zsh and bash. The operations can be combined into a function:

    phpPost() {
        php ${1:?script required} > ${2:?csv required} 2> ${3:?err required}
        [[ ! -s $2 ]] && rm $2
        [[ ! -s $3 ]] && rm $3
    }
    phpPost file.php data.csv error.txt
    

    Option 2 – save stdout and stderr in shell variables, and only create the files if there is output:

    php file.php 
        >  >(d=$(<&0) && [[ -n $d ]] && <<<$d >data.csv) 
        2> >(e=$(<&0) && [[ -n $e ]] && <<<$e >error.txt)
    

    Some of the pieces:

    • >, 2> – redirect stdout and stderr.
    • >(...) – process substitution. The shell creates a subprocess (sort of – see below) and sends the output that we’ve redirected to that process.
    • t=$(<&0) – store the stdin of the subprocess in the variable t.
    • [[ -n ... ]] – test if the string is non-empty.
    • <<<... > ... – use a here-string to write the variable contents to a file.

    This version comes with some caveats:

    • It is specific to zsh (or more accurately, it did not work in my very brief tests with bash).
    • This is not a common shell idiom.
    • The files are not overwritten each time.
    • Process substitutions can be run asynchronously by the shell, which means in some cases the initial command might finish before the output files are completely written. In this instance, the commands within the process substitution are all zsh built-ins, so the shell runs them synchronously within a single process. Other variations of this pattern might behave differently – the zsh documentation describes a workaround if that is an issue.

    This can also be written as a function:

    phpVars() {
        rm ${2:?} ${3:?} 2>/dev/null
        php ${1:?} 
            >  >(d=$(<&0) && [[ -n $d ]] && <<<$d >${2}) 
            2> >(e=$(<&0) && [[ -n $e ]] && <<<$e >${3})
    }
    phpVars file.php data.csv error.txt
    

    Alternative solutions:

    • It may be easier to detect whether the php command succeeded by testing the shell return code instead of looking at the output files.
    • If updating the php code is a possibility, that script could write directly to the data and error files instead of passing the information through the shell.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search