コマンドラインなクラス

こんにちは、らいあです。
Dinoのプログラマーは毎日いろんなツールや関数を作って遊んでいます。
僕が今日作ったクラスを紹介しますね!

[19:13:45] ryerの発言:
$cmd = new CommandLine(’rsync’);
$cmd->setOption(’delete’);
$result = $cmd->execute();
なんかこんなライブラリをどっかで見たことあるんだけど
PEARでしたっけ、知ってるしといる?

[19:26:38] r_koikeの発言:
PEAR::System_Command ?

[22:19:18] ryerの発言:
こういうのがみつからなかったので作った。そんだけ・・・。

本体:

/**
 * コマンドラインの実行をする簡易ライブラリ。
 */
class GunyuCommand
{
    private static $path = array();
 
    private $name;
    private $options = array();
    private $arguments = array();
    private $backgroundEnabled = false;
    private $stdoutEnabled = true;
    private $stderrEnabled = false;
    private $stdoutRedirect = null;
    private $stderrRedirect = null;
    private $previousPipes = null;
 
    /**
     * コマンド検索パスの追加
     * @param string $path ディレクトリ ex)"/usr/local/bin"
     */
    public static function addPath($path)
    {
        self::$path[] = $path;
    }
 
    /**
     * 生成
     * @param string $name コマンド名 ex)"mkdir"
     */
    public function __construct($name)
    {
        $this->name = $name;
    }
 
    /**
     * オプションの追加
     * @param string $option 1文字ならショートオプションとみなす。
     * @param string $arg
     */
    public function addOption($option, $arg=null)
    {
        $this->options[] = array('opt'=>$option, 'arg'=>$arg);
    }
 
    /**
     * オプションの追加
     * 引数にずらずらとオプションを並べることができます。
     * ※このメソッドではオプションの引数は取れません。
     */
    public function addOptions()
    {
        foreach (func_get_args() as $option) {
            $this->options[] = array('opt'=>$option, 'arg'=>null);
        }
    }
 
    /**
     * 引数の追加
     * @param string $arg
     */
    public function addArgument($arg)
    {
        $this->arguments[] = $arg;
    }
 
    /**
     * バックグラウンドで実行する
     * ※単にコマンドラインの最後に&をつけるだけです。
     * ※デフォルトはfalseです
     * @param boolean $enabled
     */
    public function setBackgroundEnabled($enabled)
    {
        $this->backgroundEnabled = $enabled;
    }
 
    /**
     * 標準出力をふつうに標準出力する
     * デフォルトはtrueです。falseだと/dev/null行きになります。
     * @param boolean $enabled
     */
    public function setStdOutEnabled($enabled)
    {
        $this->stdoutEnabled = $enabled;
    }
 
    /**
     * 標準出力をファイルにリダイレクトする
     * デフォルトはnullです。
     * @param string $file
     */
    public function setStdOutRedirect($file)
    {
        $this->stdoutRedirect = $file;
    }
 
    /**
     * 標準エラー出力を標準出力にリダイレクトする
     * デフォルトはfalseです。falseだと/dev/null行きになります。
     * @param boolean $enabled
     */
    public function setStdErrEnabled($enabled)
    {
        $this->stderrEnabled = $enabled;
    }
 
    /**
     * 標準エラー出力をファイルにリダイレクトする
     * ※デフォルトはnullです。
     * @param string $file
     */
    public function setStdErrRedirect($file)
    {
        $this->stderrRedirect = $file;
    }
 
    /**
     * パイプでつながった新しいGunyuCommandインスタンスを返します。
     *
     * see below:
     *   $this | $cmd
     *
     * @return GunyuCommand $cmd
     */
    public function pipe(GunyuCommand $cmd)
    {
        $newCmd = clone $cmd;
        $newCmd->previousPipes = $this;
        return $newCmd;
    }
 
    /**
     * 実行
     * @return GunyuCommandResult バックグラウンド実行の場合にはnullが返ります。
     */
    public function execute()
    {
        $cmdLine = $this->getCommandLine();
        exec($cmdLine, $stdout, $exitCode);
        return new GunyuCommandResult($exitCode, $stdout);
    }
 
    /**
     * コマンドラインを返す
     * 実際のところこれをexecできます。
     * @return string
     */
    public function getCommandLine()
    {
        $prevCmd = '';
        if ($this->previousPipes) {
            $prevCmd = $this->previousPipes->getCommandLine(). ' | ';
        }
 
        $path = '';
        if (self::$path) {
            foreach (self::$path as $it) {
                if (is_executable($it.'/'.$this->name)) {
                    $path = $it.'/';
                }
            }
        }
        $cmdName = escapeshellcmd($path.$this->name).' ';
 
        $cmdOptions = '';
        foreach ($this->options as $it) {
            if (strlen($it['opt']) >= 2) {
                $h = '--';
                $sep = ($it['arg']===null) ? '' : '=';
            } else {
                $h = '-';
                $sep = ($it['arg']===null) ? '' : ' ';
            }
            $cmdOptions .= '"'. $h. escapeshellcmd($it['opt']). $sep. escapeshellcmd($it['arg']). '" ';
        }
 
        $cmdArguments = '';
        foreach ($this->arguments as $it) {
            $cmdArguments .= '"'. escapeshellcmd($it). '" ';
        }
 
        $cmdStdout = '';
        if ($this->stdoutRedirect) {
            $cmdStdout = '>'.'"'.escapeshellcmd($this->stdoutRedirect).'" ';
        } else if ($this->stdoutEnabled) {
            $cmdStdout = '';
        } else {
            $cmdStdout = '>/dev/null ';
        }
 
        $cmdStderr = '';
        if ($this->stderrRedirect) {
            $cmdStderr = '2> '.'"'.escapeshellcmd($this->stderrRedirect).'" ';
        } else if ($this->stderrEnabled) {
            $cmdStderr = '2>&1 ';
        } else {
            $cmdStderr = '2>/dev/null ';
        }
 
        $cmdBg = '';
        if ($this->backgroundEnabled) {
            $cmdBg = '&';
        }
 
        return trim($prevCmd.$cmdName.$cmdOptions.$cmdArguments.$cmdStdout.$cmdStderr.$cmdBg);
    }
 
    /**
     * same getCommandLine
     */
    public function __toString()
    {
        return $this->getCommandLine();
    }
}
 
/**
 * コマンドラインの実行結果。
 * イミュータブルオブジェクトです。
 */
class GunyuCommandResult
{
    private $exitCode;
    private $stdout;
 
    /**
     * 生成
     * @param int $exitCode 終了コード
     * @param array $stdout 標準出力
     */
    public function __construct($exitCode, $stdout)
    {
        $this->exitCode = $exitCode;
        $this->stdout = $stdout;
    }
 
    /**
     * getExitCode()==EXIT_SUCCESS
     * @return boolean
     */
    public function isSuccess()
    {
        return ($this->exitCode == 0);
    }
 
    /**
     * 終了コードを返す
     * @return int
     */
    public function getExitCode()
    {
        return $this->exitCode;
    }
 
    /**
     * 標準出力を文字列で返す
     * 各行は0x0Aで接続されています。
     * @return string
     */
    public function getStdOut()
    {
        return join("\x0A", $this->stdout);
    }
 
    /**
     * 標準出力を1行ずつの配列で返す
     * @return array
     */
    public function getStdOutAsArray()
    {
        return $this->stdout;
    }
}

使いかた:

$cmd1 = new GunyuCommand('ls');
$cmd1->addOptions('a', 'l');
$cmd1->addArgument('/');
 
$cmd2 = new GunyuCommand('grep');
$cmd2->addArgument('u');
 
$cmd3 = new GunyuCommand('grep');
$cmd3->addArgument('e');
 
$cmd4 = new GunyuCommand('grep');
$cmd4->addArgument('l');
 
$result = $cmd1->pipe($cmd2)->pipe($cmd3)->pipe($cmd4)->execute();
echo $result->getStdOut();

結果:

-rw-r--r--  1 root    root        0 Feb 22 20:40 .autorelabel
drwx------  2 root    root    16384 Feb 22 20:31 lost+found
drwxr-xr-x  2 root    root     4096 Feb 22 20:31 selinux

※こんな感じのコマンドが実行されています:

ls “-a” “-l” “/” 2>/dev/null | grep “u” 2>/dev/null | grep “e” 2>/dev/null | grep “l” 2>/dev/null

コメント / トラックバックはありません

コメントする