引言
今年(2016)年初來到A公司了,剛入職的時候比較缺前端fe,就過來頂了三個月,這段時間學到了好多前端開發的知識,這些都是題外話了。期間接觸了一個很好用的前端自動化部署工具 —— fis,其中有個小功能特別好用,實時監控文件,然后同步到服務端,大大提高了開發的效率。之后轉到后端,發現大家開發測試相同模塊的時候經常會出現互相覆蓋的情況,要么就一個個的復制文件到服務器上,效率非常低下,之前為這個也開發過一個純shell版本的自動化監控同步工具文件同步監控工具后來發現純shell版本的使用體驗和擴展性能非常差,就開發了這篇文章所要介紹的php版本。
這次開發了一個規范的php composer包,使用的時候直接require即可。
實現
php版本的實現沿用了shell版本的思想,通過管道命令傳遞shell命令的結果,然后對結果做各種處理,達到我們監控文件的目的。
為了完成這個功能,首先要實現一個php版本的管道命令,這里我對resource popen ( string $command , string $mode )
做了封裝,可以通過很友好的處理命令的返回值。
完成了上面的模塊,接下來就是具體的實現了,實現這里有多重友好的方式,添加多路徑遞歸監控、包含正則匹配、排除正則匹配。
pipe的開發
<?php
/**
* php對對popen的封裝,通過回調的方式模擬管道命令
*
*/
namespace Aizuyan\Pipe;
class Pipe
{
/**
* 要通過管道執行的命令
*/
protected $command = "";
/**
* 回調函數,將管道數據傳遞給該函數
*/
protected $callback = null;
/**
* 數據之間的分隔符
*/
protected $delimiter = "\n";
/**
* 設置命令
*
* @param cmd string 要運行的命令
*/
public function setCmd($cmd)
{
$this->command = $cmd;
return $this;
}
/**
* 設置回調函數,處理管道輸出的命令
*/
public function setCallback(callable $cb)
{
$this->callback = $cb;
return $this;
}
/**
* 設置數據片段之間的分隔符
*/
public function setDelimiter($delimiter)
{
$this->delimiter = $delimiter;
return $this;
}
/**
* 開始運行
*/
public function run()
{
$fp = popen($this->command, "r");
if (false === $fp) {
throw new \RuntimeException("popen execute command failed!");
}
$item = "";
while (!feof($fp)) {
$char = fgetc($fp);
if ($this->delimiter == $char) {
call_user_func($this->callback, $item);
$item = "";
} else {
$item .= $char;
}
}
pclose($fp);
}
}
下面是測試程序
<?php
include "vendor/autoload.php";
$test = new \Ritoyan\Pipe\Pipe();
$test->setCmd("tail -f /root/t.txt")->setCallback(function($item){
echo "獲取內容:" . $item . "\n";
})->setDelimiter("\n")->run();
對tail -f /root/t.txt
這個shell命令的返回結果進行實時處理,setCallback(callable func)
設置了回調函數,func的參數是shell命令的標准輸出,setDelimiter($delimiter)
設置了傳入回調函數參數的分隔符,這樣就可以很容易、很任性的將輸出傳遞給回調函數了。
inotify開發
這部分代碼就不提出來了,主要就是依賴上面的Pipe
然后對inotifywait -mrq --format '%w,%e,%f'
命令的輸出做處理,正向逆向過濾,下面是對Inotify
的調用
<?php
require "vendor/autoload.php";
$obj = new Aizuyan\Inotify\Inotify();
$obj->addExclude([
"/swp$/",
"/swpx$/",
"/~$/",
"/\d$/",
"/swx$/"
])->setCallback(function ($item){
echo $item["event"] . " 文件 " . $item["file"] . "\n";
})->addPaths("/datas/git/")->start();
這樣運行之后,到我們修改/datas/git
目錄下的文件的時候會輸出下面的內容,可以很方便的對修改文件做定制化的處理
CREATE 文件 /datas/git/inotify/README.md
MODIFY 文件 /datas/git/inotify/README.md
MOVED_TO 文件 /datas/git/aizuyan/pinyin-1/README.md
DELETE 文件 /datas/git/aizuyan/pinyin-1/LICENSE
......
inotify-tools安裝
整個功能依賴於一個linux軟件 —— inotify-tools
centos安裝yum install inotify-tools
,或者通過源碼直接安裝(文檔)嘗試在OS中安裝,發現失敗了~
如何使用
我已經將他發布到了composer倉庫中,可以輕松安裝:
composer require aizuyan/inotify
,之后就可以像上面的例子一樣使用了
另外這是開發的兩個組件的github地址:Aizuyan\Pipe\Pipe , Aizuyan\Inotify\Inotify