Impossible to redirect subprocess stderr on macOS

command lineterminal

Running following in Terminal on macOS Mojave

php -r 'foo();' 2>/dev/null

Produces

Fatal error: Uncaught Error: Call to undefined function foo() in Command line code on line 1

Error: Call to undefined function foo() in Command line code on line 1

Call Stack:
    0.0002     390016   1. {main}() Command line code:0

But on Ubuntu Xenial and FreeBSD it produces no output, which is expected, as I am silencing the error. I tried with zsh 5.7.1, default bash 3.2.57 and bash 5.0.11. I'm interested why is this the case on macOS.

Another interesting thing here is that when redirecting both stdout and stderr, then there is finally no output. But redirecting neither stdout nor stderr has the silence effect.

Some more info:

➜  ~ type php
php is /usr/local/bin/php
➜  ~ file $(type -p php)
php:                cannot open `php' (No such file or directory)
is:                 cannot open `is' (No such file or directory)
/usr/local/bin/php: Mach-O 64-bit executable x86_64
➜  ~

Best Answer

This particular behaviour have nothing at all to do with any "impossibility" of redirecting subprocess stderr on macOS. It is not impossible, and works like you would expect - i.e. similar to Linux and FreeBSD.

The gotcha here is that you're using different PHP configurations on those setups. It seems you're not using the stock supplied PHP, but rather something else - but I'll explain according to the stock PHP considering that the same will apply to a custom install.

The way PHP works is that it outputs these types of errors messages according to the setting of the "display_errors" configuration directive. You can choose to either mute it completely (i.e. never output the error), output to stdout (the default) or output to stderr.

You'll commonly set the value of display_error in /etc/php.ini - if you haven't got a php.ini file, or display_errors is not set in it, it will default to stdout. If you're using a custom built php, the location of the php.ini file will vary.

You can test this out by performing the following in the Terminal:

php -r 'foo();' 2>/dev/null

Produces

Fatal error: Uncaught Error: Call to undefined function foo() in Command line code:1
Stack trace:
#0 {main}
  thrown in Command line code on line 1

However running:

php -d'display_errors=stderr' -r 'foo();' 2>/dev/null

Produces no output.

You can also use 1>/dev/null to redirect stdout, and then you'll see that the behaviour reverses between those two examples.

Note also that the setting "on" for display_errors is the same as "stdout".