I’m trying to run the curl
command in PHP file and am trying to list its output in the error log file. All the PHP errors are listed in the file except the errors I get from exec()
function. The PHP code I’m trying is:
exec("nohup curl --output " . $_SERVER['DOCUMENT_ROOT'] . "/abc.mp3 http://192.99.8.170:8098/stream/1; --max-time $time_in_seconds > /devnull&");
How could I get the error generated by this command in the error log file? I have googled for this, but I couldn’t get sufficient results.
Advertisement
Answer
The command
There are several issues in the command itself.
exec(“nohup curl –output “.$_SERVER[‘DOCUMENT_ROOT’] . “/abc.mp3 http://192.99.8.170:8098/stream/1; –max-time $time_in_seconds > /devnull&”);
(I have wrapped the original line for clarity.)
No need in nohup
The main purpose of the nohup
command is to have a process running if you get logged off the shell because of a terminal line hang-up. (When a shell exits, it sends SIGHUP to its child processes.) With nohup
you can start a command and then log off the shell.
The curl
command is indeed terminated on SIGHUP. You may use nohup
to put the download process running in background. But your objective is to capture the command’s output, whether STDIN or STDOUT. It is also important to know when the command completes its work and how. For instance, after executing the command synchronously, it is possible to check the exit code (zero indicates a success) and to check if the file is actually downloaded to the file system:
$output_dir = empty($_SERVER['DOCUMENT_ROOT']) ? '/tmp/default-download-dir' : $_SERVER['DOCUMENT_ROOT']; if (!is_dir($output_dir)) { if (!mkdir($output_dir, 0755, true)) die("Failed to create directory $output_dir"); } $url = 'http://php.net/images/logo.php'; $output_file = "{$output_dir}/logo.svg"; ; // 2>&1 redirects standard error descriptor (2) // to the standard output descriptor (1) $command = sprintf("curl -o %s %s 2>&1", escapeshellarg($output_file), escapeshellarg($url)); $output = []; exec($command, $output, $exit_code); if ($exit_code != 0) { die("Failed to download $url to $output_file"); } clearstatcache(true, $output_file); if (!file_exists($output_file)) { die("File $output_file not found on file system, command: $command"); } echo "$url has been saved to $output_file:n", implode(PHP_EOL, $output), PHP_EOL;
Output
http://php.net/images/logo.php has been saved to /tmp/default-download-dir/logo.svg: % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 2202 100 2202 0 0 4171 0 --:--:-- --:--:-- --:--:-- 4665
Note the use of escapeshellarg
function. You should escape all shell arguments, otherwise the shell will interpret its special characters.
The semicolon
Strings separated with semicolon (;
) are interpreted as different commands. So your --max-time $time_in_seconds > /devnull&
is interpreted as the second command. I have no idea what you have tried to achieve, but you most likely don’t need it there.
Redirection to /dev/null
There is a typo in your > /devnull&
expression. You likely meant redirection to the /dev/null
pseudo-device: > /dev/null
.
Capturing the output to different file descriptors
In the code above, we have captured the standard output. You should use the proc_open()
function, if you want to capture the output to other file descriptors as well:
$output_dir = empty($_SERVER['DOCUMENT_ROOT']) ? '/tmp/default-download-dir' : $_SERVER['DOCUMENT_ROOT']; if (!is_dir($output_dir)) { if (!mkdir($output_dir, 0755, true)) die("Failed to create directory $output_dir"); } $url = 'http://php.net/images/logo.php'; $output_file = "{$output_dir}/logo.svg"; ; $command = sprintf("curl -o %s %s", escapeshellarg($output_file), escapeshellarg($url)); $descriptors = [ 1 => ['pipe', 'w'], // stdout 2 => ['pipe', 'w'], // stderr ]; $proc = proc_open($command, $descriptors, $pipes); if (!is_resource($proc)) die("Failed to open process for command $command"); if ($output = stream_get_contents($pipes[1])) echo "Output: $outputnn"; fclose($pipes[1]); if ($errors = stream_get_contents($pipes[2])) echo "Errors: >>>>>>>>n$errorsn<<<<<<<nn"; fclose($pipes[2]); $exit_code = proc_close($proc); if ($exit_code != 0) { die("Failed to download $url to $output_file"); } clearstatcache(true, $output_file); if (!file_exists($output_file)) { die("File $output_file not found on file system, command: $command"); } echo "$urlnhas been saved to $output_filen";
Output
Errors: >>>>>>>> % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2202 100 2202 0 0 4231 0 --:--:-- --:--:-- --:--:-- 4735 <<<<<<< http://php.net/images/logo.php has been saved to /tmp/default-download-dir/logo.svg
(curl
outputs the “verbose” output to the standard error descriptor)
The fixed command
In the view of foregoing, your command should be built as follows:
$url = 'http://192.99.8.170:8098/stream/1'; $command = sprintf("curl -o %s %s --max-time $time_in_seconds", escapeshellarg($output_file), escapeshellarg($url));