一点背景信息,这个问题是这个问题的后续问题:Bash 远程自动完成:更改“起始”目录
无论如何,我正在编写自定义自动完成 bash 脚本:我希望自动完成功能像cd
以前一样工作,只是我想从特定目录(不一定是当前目录)获取名称。除非文件名中含有空格,否则它的效果很好。
一个简单的例子。假设我从中获取名称的目录中有两个文件:a_file
和another file
(注意空格)。有时候是这样的:
my_command
TABTAB
a_file
file
another
该演示文稿并不完美,但其想法是提示我 3 个选择,another file
分为another
和file
。所需的输出将是:
file_1
another file
。我还希望自动转义空格:
my_command ano
TAB
my_command another\ file
我的脚本如下所示:
#!/bin/bash _get_file_list() { dir=“/一些/路径/” cd $目录 查找*-最大深度0 } _GetOptMyCommand() { 本地电流 完全=() cur=${COMP_WORDS[COMP_CWORD]} 案例“$cur” -*) COMPREPLY=( $( compgen -W "-h -l --help --list --" -- "$cur" ) );; *) COMPREPLY=( $( compgen -W "$(_get_file_list)" -- "$cur" ) );; 埃萨克 返回0 } 完整 -F _GetOptMyCommand my_command
如何处理文件名中的空格并使我的自动完成脚本像这样cd
?
答案1
相信在这种情况compgen
下使用它可能会更好。find
您可能已经有一个系统完成脚本。尝试例如
locate bash_completion
在 Debian 变体上,这可能是:
/usr/share/bash-completion/bash_completion
你在哪里找到例如_filedir
。因此,最简单的方法是:
*)
pushd "/some/path" >/dev/null
_filedir
popd >/dev/null
如果这不是一个选择,这可能是一个入门:
_comp_by_path()
{
local opt cur dir
local IFS=$'\n' x tmp
local -a tokens
opt="$1"
cur="$2"
dir="$3"
# Enter target directory
pushd "$dir" >/dev/null
# Get directories, filtered against current
[[ "$opt" != "-f" ]] && \
x=$( compgen -d -- "$cur" ) &&
while read -r tmp; do
tokens+=( "$tmp" )
done <<< "$x"
# Get files, filtered against current
[[ "$opt" != "-d" ]] && \
x=$( compgen -f -- "$cur" ) &&
while read -r tmp; do
tokens+=( "$tmp" )
done <<< "$x"
# If anything found
if [[ ${#tokens[@]} -ne 0 ]]; then
# Make sure escaping is OK
compopt -o filenames 2>/dev/null
COMPREPLY+=( "${tokens[@]}" )
fi
# Go back
popd >/dev/null
}
_GetOptMyCommand()
{
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-h -l --help --list --" -- "$cur" ) );;
*)
_comp_by_path "any" "$cur" "/some/path"
esac
}
complete -F _GetOptMyCommand my_command
使用的变体find
可能是这样的:
_zaso()
{
local dir="$1"
pushd "$dir" >/dev/null
find * -maxdepth 0 2>/dev/null
popd >/dev/null
}
_comp_with_find()
{
local cur dir
local IFS=$'\n'
cur="$1"
dir="$2"
compopt -o filenames 2>/dev/null
COMPREPLY=( $( compgen -W "$(_zaso "$dir")" -- "$cur" ) );
}
另请注意,printf
Bash 中有一个%q
选项。因此,要生成带引号的字符串,可以使用以下选项:
find * -maxdepth 0 2>/dev/null && \
while read -r tmp; do
printf "%q\n" "$tmp"
done <<< "$x"
另外,文件名也不能包含换行符,其中很多字符都会被破坏。还没有找到使用\0
with 的方法compgen
。
答案2
我最近遇到了同样的问题。我能够通过更改 IFS 变量local IFS=$'\n'
然后使用数组来使事情正常工作。尝试使用这个:
_GetOptMyCommand(){
# Backup old nullglob setting
local shoptbakup="`shopt -p nullglob`"
shopt -s nullglob
local cur opts i opt
local IFS=$'\n'
cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
-*)
opts=("-h" "-l" "--help" "--list");;
*)
# Get Files
opts=(${cur}*)
esac
COMPREPLY=( $( compgen -W "${opts[*]}" -- "$cur" ) )
# Restore nullglob setting
eval "$shoptbakup" 2>/dev/null
return 0
}
complete -o filenames -o bashdefault -F _GetOptMyCommand my_command
答案3
尝试find
通过管道输出sed 's/ /\\ /'
。
但请注意,其他字符(引号、$、&..所有常见的字符)也可能会让您陷入困境。
sed 's/\([ $&!#*()<>|{}[?`"'"'"']\)/\\\1/'
可能会更好,因为它会转义大多数“特殊”字符。
(不过,我还没弄清楚如何正确转义 \ )