Bash参数自动完成

圣安东尼奥

我正在写bash来处理目录中的文件。我有一些基本(相当长的)路径要处理。我想通过选项和参数指定一个子路径并使它自动完成。我的意思是这样的:

#!/bin/bash

dir=~/path/to/base/dir

while getopts ":d:" opt; do
    case $opt in
      d)
         dir=$dir/$OPTARG
         ;;
      #invalid input handling
    esac
done

但是在此实现中,参数不是自动完成的,我必须自己键入子目录的所有名称(Tab不起作用)。

有什么办法可以做到这一点吗?

每一个

是的,可以从命令行选项标志指定的自定义基本目录中实现路径补全。这是一个说明如何完成此操作的小示例。首先,我将略微修改您的示例脚本,以使演示更加有趣(即产生输出):

#!/bin/bash

# echo_path.sh
#
#    Echoes a path.
#

# Parse command-line options
while getopts ":d:" opt; do

    # Use '-d' to specify relative path
    case "${opt}" in
    d)
        directory="${OPTARG}"
        ;;
    esac
done

# Print the full path
echo "$(readlink -f ${directory})"

这基本上与您的示例脚本相同,但是会打印出给定的参数。

接下来,我们需要编写一个由Bash程序完成系统调用的函数。这是定义此类功能的脚本:

# echo_path_completion.bash

# Programmatic path completion for user specified file paths.

# Define a base directory for relative paths.
export BASE_DIRECTORY=/tmp/basedir

# Define the completion function
function _echo_path_completion() {

    # Define local variables to store adjacent pairs of arguments
    local prev_arg;
    local curr_arg;

    # If there are at least two arguments then we have a candidate
    # for path-completion, i.e. we need the option flag '-d' and
    # the path string that follows it.
    if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then

        # Get the current and previous arguments from the command-line
        prev_arg="${COMP_WORDS[COMP_CWORD-1]}";
        curr_arg="${COMP_WORDS[COMP_CWORD]}";

        # We only want to do our custom path-completion if the previous
        # argument is the '-d' option flag
        if [[ "${prev_arg}" = "-d" ]]; then

            # We only want to do our custom path-completion if a base
            # directory is defined and the argument is a relative path
            if [[ -n "${BASE_DIRECTORY}" && ${curr_arg} != /* ]]; then

                # Generate the list of path-completions starting from BASE_DIRECTORY
                COMPREPLY=( $(compgen -d -o default -- "${BASE_DIRECTORY}/${curr_arg}") );

                # Don't append a space after the command-completion
                # This is so we can continue to apply completion to subdirectories
                compopt -o nospace;

                # Return immediately
                return 0;
            fi
        fi
    fi

    # If no '-d' flag is given or no base directory is defined then apply default command-completion
    COMPREPLY=( $(compgen -o default -- "${curr_arg}") );
    return 0;
}

# Activate the completion function
complete -F _echo_path_completion echo_path

现在让我们获取完成脚本的源代码:

source echo_path_completion.bash

让我们使脚本可执行,并将其移动到PATH中的某个位置:

chmod +x echo_path.bash
mv -i echo_path.bash /usr/local/bin

最后,让我们为脚本添加一个别名,该别名没有文件扩展名:

alias echo_path=echo_path.bash

现在,如果您输入echo -d并单击选项卡按钮,则应该从BASE_DIRECTORY开始获取文件路径的完成。要对此进行测试,您可以尝试以下操作:

mkdir -p ${BASE_DIRECTORY}
mkdir -p "${BASE_DIRECTORY}/file"{1..5}

当您点击选项卡时,您应该获得以下完成列表:

user@host:~$ echo_path -d
user@host:~$ echo_path -d /tmp/basedir/file
/tmp/basedir/file1  /tmp/basedir/file2  /tmp/basedir/file3  /tmp/basedir/file4  /tmp/basedir/file5

请注意,在第一个选项卡之后,字符串将转换为绝对路径。您可以根据需要更改此设置,但我认为这可能是更可取的做法。

这里是一些参考资料,您可以参考以获取更多信息。

有关官方参考,请查看Bash手册的以下部分:

另请参阅Linux Documentation Project的Advanced Bash脚本指南:

要快速了解Bash中的程序化补全的某些功能,请参阅“ The Geek Stuff”网站上的这篇文章:

还有一些相关的StackOverflow帖子可能对您有用:

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章