aross
aross

Reputation: 3606

Get exit status of piped commands in PHP

I've got a PHP script that calls the system shell with a piped command. In this case we're talking about a backup script (but it could be anything, I'm asking specifically about the exit status!):

exec(
    "mysqldump --user=$u --password=$p --host=$h --port=$p $db | gzip -9 > backup.sql.gz",
    $out,
    $status
);

Now I want to know if the mysqldump command yielded an error, but the $status variable always seems to contain 0, even if I force an error. It appears to be the exit code of the second command (gzip in this case). I want to be able to see the exit status of the first command in PHP.

Upvotes: 1

Views: 743

Answers (2)

aross
aross

Reputation: 3606

I've managed to think of a more generic solution that makes the exit status of each command in the pipe available to PHP. Of course it requires a shell with $PIPESTATUS (this excludes plain sh).

// The command with pipes.
$command = 'command1 | command2 | echo Test | gzip -9 -f';

// Execute the command. The overall exit code is in $exitStatus.
exec(
    $command . '; echo -e "\n"${PIPESTATUS[*]}',
    $out,
    $exitStatus
);

// Get the exit statuses and remove them from the output.
$pipeStatus = explode(' ', array_pop($out));

print_r([$pipeStatus, $out]);
// [
//   [
//     "127",
//     "127",
//     "0",
//     "0",
//   ],
//   [
//     b"\x1F‹\x089fÙW\x02I-.á\x02Â\x1Axú\x05",
//   ],
// ]

Slightly simpler variant if you're sure the piped commands will end with a newline (notice the echo part in the command is different):

// The command with pipes.
$command = 'command1 | command2 | echo Testing things | sed s/things/stuff/';

// Execute the command. The overall exit code is in $exitStatus.
exec(
    $command . '; echo ${PIPESTATUS[*]}',
    $out,
    $exitStatus
);

// Get the exit statuses and remove them from the output.
$pipeStatus = explode(' ', array_pop($out));

print_r([$pipeStatus, $out]);
// [
//   [
//     "127",
//     "127",
//     "0",
//     "0",
//   ],
//   [
//     "Testing stuff",
//   ],
// ]

Upvotes: 0

BA_Webimax
BA_Webimax

Reputation: 2679

You'll need a little help from the Bash internal array PIPESTATUS. This holds the exit status of each command in the pipe. Since you're looking for the first command's exit status you would be addressing PIPESTATUS[0]. So you're code would look like:

exec(
    "bash -c 'mysqldump --user=$u --password=$p --host=$h --port=$p $db | gzip -9 > backup.sql.gz; exit \${PIPESTATUS[0]}'",
    $out,
    $status
);

Note, this changes the overall exit status of the exec() call and you'll need additional code if you want to catch a failure in a longer chain of commands.

Upvotes: 3

Related Questions