在bash脚本中自动完成文件名

4
我正在尝试模仿bash文件自动补全功能。
假设我有以下文件:
test1
test2
当输入字符串为“te”时,我希望得到输出“test”。
这是我当前的尝试($c是输入字符串):
l=1
q="$c"
for j in $(ls -A | grep "^$c"); do
    if [ "${j/$c}" != "$j" ]; then 
            n=$(ls -A | grep ^$j | wc -l)
            if [ $n -gt $l ]; then 
                    q="$j"  
            fi      
    fi
done
c="$q"
echo $c

感谢任何帮助

(这是需要翻译的内容)

1
报错随处可见,“ls”解析……我不知道这怎么可能成功。但问题很好!如果可以知道如何在脚本中实现Bash自动补全,而不必重复造轮子,那将是非常棒的。 - Aleks-Daniel Jakimenko-A.
3个回答

1
我倾向于认为从完成引擎中获取这个是不可能的,因为它不是GNU Bash的一部分,而是Readline。但至少我们可以使用compgen获取可能的完成列表。找到最长公共前缀的实现应该不是问题。所以...
#!/bin/bash

SCRIPTNAME="${0##*/}"
USAGE="Usage: $SCRIPTNAME <prefix>

Print common prefix of possible file name completions. Like <TAB> but to
stdout."

(( $# == 1 )) || { printf >&2 '%s\n' "$USAGE"; exit 1; }

PREFIX="$1"

commonprefix() {
    (( $# >= 2 )) || { 
        echo "$1"
        return 0
    }
    local -i i N M
    for ((i=0; i<=${#1}; i++)); do 
        for ((N=1; N<=$#-1; N++)); do
            let M=$N+1
            [[ ${!N:i:1} == ${!M:i:1} ]] || break 2
        done
    done
    echo "${1:0:i}"
}

readarray -t COMPLETIONS < <(compgen -f "$PREFIX")
commonprefix "${COMPLETIONS[@]}"

谢谢你的回答,但它并不完全正确。我尝试在我的bin文件夹中使用“ui”,但输出为“uim-”,尽管还有另一个名为“uic”的文件。感叹号的东西非常酷,将来可能会有用。 - Yggi
@Yggi 是的,你说得对。我没有使用默认的TAB补全,所以我误解了任务。已经修复了。我希望现在它能够完全符合你的期望。 - Dmitry Alexandrov

1
尽管 Dmitry Alexandrov 已经提供了更好的解决方案,但我仍想发布自己的解决方案,这是我在等待答案时制作的:
l=1
while [ -n $l ]; do
    l=${#c}
    a=$(ls -A | grep "^$c" | wc -l)
    q=$c
    for i in $(ls -A | grep "^$q"); do
            if [ $i == $q ]; then 
                    unset l
                    break
            else
                    v=$(ls -A | grep "^$q${i:$l:1}" | wc -l)
                    if [ $v == $a ]; then 
                            q="$c${i:$l:1}"
                            break
                    fi
            fi
    done
    if [ $c == $q ]; then break; fi
    c=$q
done
echo $c

它能够适用于我所有的测试,但速度较慢(虽然可以进行优化)。

0

只是为了表明你的思路是正确的,我让你的代码运行起来了:

#!/bin/bash

c=$1
q="$c"
for j in $c*; do
    if [ "${j/$c}" != "$j" ]; then
        startn=$(ls -1A | grep -c "^${j:0:$((${#c} + 1))}")
        for (( i=${#c}; i <= ${#j}; i++ )); do
            n=$(ls -1A | grep -c "^${j:0:$i}")
            if [ "$n" -lt "$startn" ]; then
                q="${j:0:$((i - 1))}"
            elif [ "$n" -le "$startn" ]; then
                q="${j:0:$i}"
            fi
        done
    fi
done
c="$q"
echo "$c"

但这只是一个概念验证,不要使用它。请参考Dmitry Alexandrov的答案获取一个好的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接