我正在编写一个 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
不起作用)。
有没有办法在 bash 中实现这一目标?
答案1
是的,可以从命令行选项标志指定的自定义基目录实现路径完成。这是一个小例子,说明如何实现这一点。首先,我将稍微修改您的示例脚本,以使演示稍微更有趣(即产生输出):
#!/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
让我们使脚本可执行并将其移动到路径中的某个位置:
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}
当您点击 Tab 时,您应该会得到以下完成列表:
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 手册的以下部分:
部分: 5.2 Bash 变量(查找带有
COMP_
前缀的变量)
另请参阅 Linux 文档项目中的高级 Bash 脚本指南:
有关 Bash 中编程完成的一些功能的快速介绍,请参阅“The Geek Stuff”网站上的这篇文章:
还有一些相关的 StackOverflow 帖子可能对您有用: