除了 command 子命令结构之外,Bash 是否还支持 command.subcommand 结构?如果是,如何将其合并到 bash 脚本中?

除了 command 子命令结构之外,Bash 是否还支持 command.subcommand 结构?如果是,如何将其合并到 bash 脚本中?

在我的本地计算机上安装 microk8s(Micro Kubernetes)后,我遇到的命令之一是microk8s.enable dns它也可以作为microk8s enable dns.这似乎不是一个普遍的事情。git status是有效命令,但git.status不是。 Linux 系统如何支持这种类型的命令结构?如何将这种行为合并到我的 Bash 脚本中?

答案1

有时您会看到程序(和脚本)检查用于调用程序的文件名并根据该文件确定行为。

例如,考虑这个文件和符号链接:

$ ls -l
-rwxr-xr-x  ... foo
lrwxr-xr-x  ... foo.bar -> foo

以及脚本的内容foo

#!/bin/bash

readonly command="$(basename "${0}")"
subcommand="$(echo "${command}" | cut -s -d. -f2)"

if [[ "${subcommand}" == "" ]]; then
    subcommand="${1}"
fi

if [[ "${subcommand}" == "" ]]; then
    echo "Error: subcommand not specified" 1>&2
    exit 1
fi

echo "Running ${subcommand}"

该脚本解析命令名称以查找子命令(基于问题中的点表示法)。这样,我可以运行./foo.bar并获得与运行相同的行为./foo bar

$ ./foo.bar
Running bar

$ ./foo bar
Running bar

需要明确的是,我不知道这就是microk8s.enable正在做的事情。您可以执行ls -li $(which microk8s.enable) $(which microk8s)并比较这些文件。其中一个是另一个的链接吗?如果不是,它们的 inode 号是否相同?

答案2

这已成为一种相当常见的提供工具的方式,该工具可以根据所使用的“子命令”执行多个操作。据我所知,它并没有以任何方式标准化,并且在将基本命令名称和子命令写在一起时使用点作为它们之间的分隔符对于这些工具来说绝对不是通用的。

有些工具只能使用子命令来调用,例如gitgit如果单独调用,它本身会提供帮助文本),但提供手册对于类似的子命令man command-subcommand(就像子命令的情况一样git)。

您显然已经找到了一个可以称为command-subcommand(但带有点)的工具或者作为command subcommand。在这种情况下,您可能会发现基本命令和每个组合命令都是指向同一个文件的符号链接或硬链接。

程序(无论是脚本还是编译的二进制文件)可以轻松地通过调用它的名称和参数来检查它,并相应地调整其操作。

下面是一个虚构命令的示例process,它可以将子命令作为第一个参数,如 中process action,或使用子命令调用,如process-action

该脚本实现的子命令为compiledebug、 、mogrify

#!/bin/sh

basecmd=process         # base command name
cmd=${0##*/}            # get command name ( basename "$0" )
subcmd=                 # no sub command yet

# Now pick out the sub command from the command name,
# or from the first argument.  Then fail if unsuccessful.

case $cmd in
        "$basecmd"-*)   # has sub command in command name
                subcmd=${cmd#$basecmd-}
esac

if [ -z "$subcmd" ] && [ "$#" -ge 1 ]; then
        # called as "process action"
        # rather than as "process-action"
        subcmd=$1
        shift   # remove sub command from argument list
fi

if [ -z "$subcmd" ]; then
        echo 'No action' >&2
        exit 1
fi

# Act on the sub command.
# Each action would probably be implemented as a function,
# possibly called as
#     somefunction "$@"
# ... passing the remaining command line argument to it.

case $subcmd in
        compile)        # do "compile" action
                echo 'compile'
                ;;
        debug)          # do "debug" action
                echo 'debug'
                ;;
        mogrify)        # do "mogrify action"
                echo 'mogrify'
                ;;
        *)
                printf 'Invalid action "%s"\n' "$subcmd" >&2
                exit 1
esac

我为 POSIX 编写了这个,因为它的工作sh不需要任何神秘的东西。 bashAC 程序将以类似的方式执行操作,就像用任何其他编译或解释语言编写的程序一样。这也不需要Linux;我正在 OpenBSD 上编写和测试它,它应该适用于任何 POSIX 系统。

与此基本process脚本一起的是一组硬链接或符号链接,每个子命令一个链接。在这里,我选择创建硬链接:

$ ls -li
total 8
244420 -rwxr-xr-x  4 kk  wheel  538 May  9 21:55 process
244420 -rwxr-xr-x  4 kk  wheel  538 May  9 21:55 process-compile
244420 -rwxr-xr-x  4 kk  wheel  538 May  9 21:55 process-debug
244420 -rwxr-xr-x  4 kk  wheel  538 May  9 21:55 process-mogrify

这些名称中的每一个都只是同一脚本的另一个名称。

试运行:

$ ./process mogrify
mogrify
$ ./process-mogrify
mogrify
$ ./process
No action
$ ./process-compile
compile
$ ./process compile
compile
$ ./process compilee
Invalid action "compilee"

相关内容