programster/command

一个用于创建CLI命令的库。

0.2.6 2022-09-01 14:03 UTC

This package is auto-updated.

Last update: 2024-08-29 06:02:07 UTC


README

一个用于简化PHP中CLI命令创建的包,支持BASH自动补全。

执行参数

在编写你的execute函数时,请注意选项和开关的键始终是长名称,且不带任何连字符。例如,当用户输入--shell-s时,应使用shell而不是s

选项值验证

我们不验证传递给选项的任何值,因此你需要自己进行验证。

示例

以下是一个示例,其中我编写了一个命令,该命令封装了docker-exec,允许我通过Tab键补全容器名称,然后通过ID输入它们,默认使用BASH shell,但允许用户通过提供--shell-s选项来指定他们想使用sh

class DockerEnter extends \Programster\Command\Command
{
    /**
     * This is your entrypoint for the program when it is being told to execute, and not being
     * asked by BASH for tab-completion hints. 
     * @param array $options - an associative array of option-name/value pairs provided by the user.
     * E.g. a user passing --encoding=hevc would turn into ["encoding" => "hevc"]
     * @param array $switches - an associative array of switch-name/value pairs provided by the user.
     * E.g. a user passing --recursive would turn into ["recursive" => true]
     * @param array $args - a collection of arguments passed by the user.
     * @return void
     */
    public function execute(array $options, array $switches, array $args): void
    {
        if (count($args) !== 1)
        {
            throw new Exception("You must pass the ID or name of the container you wish to enter.");
        }

        $nameOrId = $args[0];

        $info = shell_exec("docker ps --format '{{ json .}}'");
        $lines = array_filter(explode(PHP_EOL, $info));
        $handled = false;
        $shell = array_key_exists("shell", $options) ? "/bin/{$options['shell']}" : "/bin/bash";

        foreach ($lines as $line)
        {
            $containerArray = json_decode($line, true);

            if ($containerArray['ID'] === $nameOrId)
            {
                passthru("docker exec -it {$containerArray['ID']} {$shell}");
                $handled = true;
                break;
            }
            elseif ($containerArray['Names'] === $nameOrId)
            {
                passthru("docker exec -it {$containerArray['ID']} {$shell}");
                $handled = true;
                break;
            }
        }

        if (!$handled)
        {
            die("There is no container with that ID or name.");
        }
    }
    
    /**
     * Get a list of possible arguments for tab completion. In this case, we want to return a list
     * of all the running container names. 
     * @return array|null - all the hints, or an empty array/null if there are none.
     */
    public function getPossibleArgs(): ?array
    {
        $hints = [];
        $info = shell_exec("docker ps --format '{{ json .}}'");
        $lines = array_filter(explode(PHP_EOL, $info));

        foreach ($lines as $line)
        {
            $containerArray = json_decode($line, true);
            $hints[] = $containerArray['ID'];
            $hints[] = $containerArray['Names'];
        }

        return $hints;
    }
    
    
    /**
     * Returns the list of possible options (e.g. --something=value). In this case we allow the
     * user to optionally set the shell to "sh" instead of the default of "bash"
     * @return CommandOptionCollection|null
     */
    public function getOptions(): ?CommandOptionCollection
    {
        return new CommandOptionCollection(
            new BasicCommandOption("shell", "s", ["bash", "sh"])
        );
    }

    /**
     * Set the switches (E.G. --something-on). In this case we have none.  
     * @return CommandSwitchCollection|null
     */
    public function getSwitches(): ?CommandSwitchCollection
    {
        return null;
    }

    /**
     * Get a collection of any possible subcommands. In future we may wrap this DockerEnter 
     * command within a parent "DockerHelper" command, in which case this would return the
     * DockerEnter class inside a collection.
     * @return CommandCollection|null
     */
    public function getSubCommands(): ?CommandCollection
    {
        return null;
    }

    /**
     * Get the name of this command, should it ever become a subcommand of another  
     * command in future.
     * @return string
     */
    public function getName(): string
    {
        return "enter";
    }
}

// Need to call the command
$command = new DockerHelper();
$command->run();

安装命令

一旦你使用此框架构建了一个命令,你将想知道如何“安装”它,以便你可以在任何地方执行它,并且BASH自动补全功能正常工作。

放置到你的$PATH中

将命令或其符号链接放置在你的$PATH中的/usr/bin/{command name}。确保设置了可执行标志。

创建BASH自动补全文件

遗憾的是,仍然需要为你的程序创建一个自动补全文件,以便告诉BASH询问你的程序以获取Tab提示。你可以通过使用“hidden”--generate-autocomplete-file开关,如下所示,使用你的程序轻松地创建此文件

my-command --generate-autocomplete-file | sudo tee /etc/bash_completion.d/dothis-completion.bash > /dev/null

或者,你可以手动创建自己的自动补全脚本。以下是一个针对你创建的自定义命令my-command的示例。

#!/usr/bin/env bash
__my_command_completions()
{
    REGEXP="*[[:space:]]"

    if [[ ${COMP_LINE} == ${REGEXP} ]]; then
        ENDS_IN_SPACE=1
    else
        ENDS_IN_SPACE=0
    fi
    
    readarray -t COMPREPLY <<< $(my-command --autocomplete-help ${ENDS_IN_SPACE} ${COMP_LINE})
}

complete -o nospace -F __my_command_completions dothis

现在打开一个新的BASH shell,你应该能看到它正在工作!

路线图

  • 防止建议已传递的选项/开关。