自定义自动完成:处理文件名中的空格

自定义自动完成:处理文件名中的空格

一点背景信息,这个问题是这个问题的后续问题:Bash 远程自动完成:更改“起始”目录

无论如何,我正在编写自定义自动完成 bash 脚本:我希望自动完成功能像cd以前一样工作,只是我想从特定目录(不一定是当前目录)获取名称。除非文件名中含有空格,否则它的效果很好。

一个简单的例子。假设我从中获取名称的目录中有两个文件:a_fileanother file(注意空格)。有时候是这样的:

my_commandTABTAB
a_file file another

该演示文稿并不完美,但其想法是提示我 3 个选择,another file分为anotherfile。所需的输出将是: file_1 another file。我还希望自动转义空格:

my_command anoTAB
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" ) );
}

另请注意,printfBash 中有一个%q选项。因此,要生成带引号的字符串,可以使用以下选项:

find * -maxdepth 0 2>/dev/null && \
while read -r tmp; do
    printf "%q\n" "$tmp"
done <<< "$x"

另外,文件名也不能包含换行符,其中很多字符都会被破坏。还没有找到使用\0with 的方法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/'

可能会更好,因为它会转义大多数“特殊”字符。

(不过,我还没弄清楚如何正确转义 \ )

相关内容