$foo xxx<TAB> (custom completion function produces choices it finds at arbitrary levels in the dir tree) xxxYYY xxxZZZ xxxBLAH ... foo xxxYYY/<TAB> (normal directory completion proceeds from this point on,to produce something like:) foo scene/shot/element/workspace/user/...
我们想这样做是因为我们有一个大型的生产开发树(这是一个CGI生产设施),精通外壳的用户一直在导航和跳跃.抱怨是树的上层是繁琐和多余的;他们只需要在第一个学期快速搜索,找到可能的“头部”选择并从那里完成目录.似乎可编程完成可以提供一种方法来实现这一点,但它变得相当难以捉摸.
我做了几次自定义bash和tcsh完成的尝试,但是我得到的最接近的是一种“单词完成”形式,用户必须将目录级别视为带空格的单独单词(例如foo scene / shot) / element / workspace / …).我可以继续攻击我目前的脚本 – 但我一直想知道是否有一些我不理解的东西 – 这是我第一次尝试完成程序,而且这些文档和示例在shell书籍和互联网上相当薄弱.如果有任何完成大师可以让我走上正轨,我会很感激.
FWIW:这是我到目前为止所做的事情(首先是tcsh,然后是bash).请注意,静态根“/ root / sub1 / sub2 / sub3”只是搜索功能的占位符,可以在不同级别找到不同的匹配项.如果我可以让它工作,我可以稍后在搜索功能中.同样,两个示例都执行单词完成,这要求用户在每个匹配的术语后键入一个空格(我还必须删除函数中的空格以构造实际路径,然后!)
TCSH示例(注意该函数实际上是一个bash脚本):
complete complete_p2 'C@*@`./complete.p2.list.bash $:1 $:2 $:3 $:4 $:5 $:6 $:7 $:8 $:9`@@' #!/bin/bash --norc # complete.p2.list.bash - Completion prototype "p2" for shotc command # Remove spaces from input arguments ppath=`echo $@ | sed -e 's/ //g'` # Print basenames (with trailing slashes) of matching dirs for completion ls -1 -d /root/sub1/sub2/sub3/$ppath* 2>/dev/null | sed -e 's#^.*/##' | awk '{print $1 "/"}'
基础示例:
_foo() { local cur prev opts flist COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" # Get all command words so far (omit command [0] element itself),remove spaces terms=`echo ${COMP_WORDS[@]:1} | sed -e 's/ //g'` # Get basenames (with trailing slashes) of matching dirs for completion flist=`ls -1 -d /root/sub1/sub2/sub3/${terms}* 2>/dev/null | sed -e 's#^.*/##' | awk '{print $1 "/"}' | xargs echo` COMPREPLY=( $(compgen -W "${flist}" ${cur}) ) return 0 } complete -F _foo foo
_foo() { local cur prev opts flist lastword new COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" lastword="${COMP_WORDS[@]: -1}" if [[ $lastword =~ / ]] then new="${lastword##*/}" # get the part after the slash lastword="${lastword%/*}" # and the part before it else new="${lastword}" lastword="" fi flist=$( command find /root/sub1/sub2/sub3/$lastword \ -maxdepth 1 -mindepth 1 -type d -name "${new}*" \ -printf "%f\n" 2>/dev/null ) # if we've built up a path,prefix it to # the proposed completions: ${var:+val} COMPREPLY=( $(compgen ${lastword:+-P"${lastword}/"} \ -S/ -W "${flist}" -- ${cur##*/}) ) return 0 } complete -F _foo -o nospace foo
笔记:
>我认为其中一个关键是nospace选项>我觉得我已经在上面的函数中重新创建了一个轮子,可能是因为没有使用$COMP_POINT>你不是(至少)使用$prev(在我的函数中始终保持值“foo”)>使用$()而不是反引号可以改善可读性和功能性>您应该使用命令来阻止运行别名等:flist = $(命令ls -1 -d …>我使用find代替ls,因为它更适合>您可以使用-S / with compgen而不是awk命令添加斜杠>你可以使用$cur而不是$terms,因为你不需要删除空格,但我使用$lastword和$new(两个新变量)>没有必要使用xargs echo,因为带有换行符的数组工作正常>我没有使用包含空格或换行符的目录名来测试它