PHP + Linux command

要如何透過 PHP 來執行 Linux 下的指令或是寫好的 bash script .

測試環境為 CentOS 7 虛擬機 (IP: http://192.168.95.205) , Apache 安裝設定請參考 http://benjr.tw/323 , 如果使用 Nginx (Web server) 請參考 – http://benjr.tw/95761 與 PHP-FPM – http://benjr.tw/95767

使用 Ubuntu 16.04 x86_64 設定 Apache2 請參考 http://benjr.tw/95861 , web server 為 Nginx 請參考 http://benjr.tw/96633 .

參考文章 – https://blog.longwin.com.tw/2013/06/php-system-exec-shell_exec-diff-2013/

可以使用 system()、exec()、shell_exec() 這三種方式來 來執行 Linux 下的指令或是寫好的 bash script ,官方說明如下:

  1. system — Execute an external program and display the output
    string system ( string $command [, int &$return_var ] )
  2. exec — Execute an external program
    string exec ( string $command [, array &$output [, int &$return_var ]] )
  3. shell_exec — Execute command via shell and return the complete output as a string
    string shell_exec ( string $cmd )

同樣使用 Linux 指令 ls (不指定路徑時以當下 PHP 檔案路徑,通常在 HTTP Root : /var/www/html )但使用不同的函數來執行.

[root@localhost html]# vi ls.php 
<?php
echo "PHP Command : system" . '<br />';
$last_line = system('ls', $return_var);
echo '<br />' . "return_var:";
print_r($return_var);
echo '<br />' . "last_line: ";
print_r($last_line);
echo '<br /> <br />';

echo "PHP Command : exec";
exec('ls', $output, $return_var);
echo '<br />' . "return_var:";
print_r($return_var);
echo '<br />' . "output: ";
print_r($output);
echo '<br /> <br />';

echo "PHP Command : shell_exec";
$output = shell_exec('ls');
echo '<br />' . "output:";
print_r($output);
?>

執行結果如下,差別大概就是 執行結果 以及 輸出,回傳值的差別.

PHP Command : system
ls.php phpinfo.php
return_var:0
last_line: phpinfo.php

PHP Command : exec
return_var:0
output: Array ( [0] => ls.php [1] => phpinfo.php )

PHP Command : shell_exec
output:ls.php phpinfo.php 
  1. system
    $last_line = system(‘ls’, $return_var);
    執行結果:
    ls.php phpinfo.php
    回傳值:
    return_var:0
    last_line: phpinfo.php
  2. exec
    exec(‘ls’, $output, $return_var);
    回傳值:
    return_var:0
    output: Array ( [0] => ls.php [1] => phpinfo.php )
  3. shell_exec
    $output = shell_exec(‘ls’);
    回傳值:
    output:ls.php phpinfo.php

CentOS / RHEL Apache 執行 PHP 時使用 apache 這個使用者 (Ubuntu 則為 www-data),也因此 Linux 系統上的權限限制使得指令執行失敗,或是某些需要特權身份的指令也無法透過 PHP 來執行.

Linux 權限限制

[root@localhost html]# vi root.php
<?php
echo "PHP Command : exec";
exec('ls /root', $output, $return_var);
echo '<br />' . "return_var:";
print_r($return_var);
echo '<br />' . "output: ";
print_r($output);
?>

執行結果如下.

PHP Command : exec
return_var:2
output: Array ( ) 

apache 並沒有讀取 /root 的權限,關於 Linux 權限請參考 – http://benjr.tw/160 .

需特權身份指令

除了系統的權限限制外,某些需要特權身份的指令也無法透過 PHP 來執行 (PHP 是透過 apache 這個使用者來執行).

雖然有些程式設定過 SUID (Set user ID,會自動轉換成檔案所有人的身分執行.) 或是 SGID (Set group ID,會自動轉換成 Group 的身份來執行.)

這些程式可以透過 find 找出找出 -4000(SUID) -2000 (SGID) ,特殊權限使用第四個數字來表示, SUID (Set user ID :4 ) ,SGID (Set group ID : 2)

[root@localhost ~]# find / -perm -4000
[root@localhost ~]# find / -perm -2000

雖然指令 mount 有設定 SUID ,但是不提供 -t (types) 參數給一般使用者來使用.

[ben@localhost ~]$ ll /usr/bin/mount
-rwsr-xr-x. 1 root root 44320 10月 31 06:48 /usr/bin/mount
[ben@localhost ~]$ mount -t nfs 192.168.95.205:/var/ftp/pub /media/
mount: only root can use "--types" option

不過我們還是可以透過設定 sudo 來解決這個問題 (不建議把 apache 的權限開的跟 root 一樣,關於 sudo 的使用請參考 – http://benjr.tw/295 ).

下面 sudo 新增 apache 使用者有 mount 與 umount 的權限,而且不需要輸入密碼.

[root@localhost ~]# visudo
apache ALL= NOPASSWD: /bin/mount, /bin/umount

PHP 範例:使用 mount 特權指令.

[root@localhost html]# vi mount.php 
<?php
echo "Before Mount";

exec('ls /media', $output, $return_var);
echo '<br />' . "return_var:";
print_r($return_var);
echo '<br />' . "output: ";
print_r($output);

exec('sudo mount -t nfs 192.168.95.205:/var/ftp/pub /media/', $output, $return_var);

echo "<br /><br />After Mount";
exec('ls /media', $output, $return_var);
echo '<br />' . "return_var:";
print_r($return_var);
echo '<br />' . "output: ";
print_r($output);

exec('sudo umount /media/', $output, $return_var);
?>

檢視一下 /media/ 與 /var/ftp/pub 目錄內容.

[root@localhost html]# ll /media/
總計 0
[root@localhost html]# ll /var/ftp/pub
總計 0
-rw-r--r-- 1 root root 0  2月 13 21:18 mount_test
-rw-r--r-- 1 root root 0  2月 13 21:17 test

執行結果可以看到在 mount 之前的資料是空的 /media , mount 之後資料同 /var/ftp/pub .

Before Mount
return_var:0
output: Array ( )

After Mount
return_var:0
output: Array ( [0] => mount_test [1] => test ) 
沒有解決問題,試試搜尋本站其他內容

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料