要如何透過 PHP 來執行 Linux 下的指令或是寫好的 bash script .
測試環境為 CentOS 7 虛擬機 (IP: http://192.168.95.205) , Apache 安裝設定請參考 https://benjr.tw/323 , 如果使用 Nginx (Web server) 請參考 – https://benjr.tw/95761 與 PHP-FPM – https://benjr.tw/95767
使用 Ubuntu 16.04 x86_64 設定 Apache2 請參考 https://benjr.tw/95861 , web server 為 Nginx 請參考 https://benjr.tw/96633 .
參考文章 – https://blog.longwin.com.tw/2013/06/php-system-exec-shell_exec-diff-2013/
可以使用 system()、exec()、shell_exec() 這三種方式來 來執行 Linux 下的指令或是寫好的 bash script ,官方說明如下:
- system — Execute an external program and display the output
string system ( string $command [, int &$return_var ] ) - exec — Execute an external program
string exec ( string $command [, array &$output [, int &$return_var ]] ) - 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
- system
$last_line = system(‘ls’, $return_var);
執行結果:
ls.php phpinfo.php
回傳值:
return_var:0
last_line: phpinfo.php - exec
exec(‘ls’, $output, $return_var);
回傳值:
return_var:0
output: Array ( [0] => ls.php [1] => phpinfo.php ) - 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 權限請參考 – https://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 的使用請參考 – https://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 )