我有这个功能:
function! Find(name)
let l:list=system("find . -name '".a:name."' | perl -ne 'print \"$.\\t$_\"'")
let l:num=strlen(substitute(l:list, "[^\n]", "", "g"))
if l:num < 1
echo "'".a:name."' not found"
return
endif
if l:num != 1
echo l:list
let l:input=input("Which ? (CR=nothing)\n")
if strlen(l:input)==0
return
endif
if strlen(substitute(l:input, "[0-9]", "", "g"))>0
echo "Not a number"
return
endif
if l:input<1 || l:input>l:num
echo "Out of range"
return
endif
let l:line=matchstr("\n".l:list, "\n".l:input."\t[^\n]*")
else
let l:line=l:list
endif
let l:line=substitute(l:line, "^[^\t]*\t./", "", "")
execute ":e ".l:line
endfunction
command! -nargs=1 Find :call Find("<args>")
当我尝试添加一个参数时,声明变为function! Find(name, search_dir)
,当我使用:Find x y
调用vim中的函数时,它总是告诉我没有足够的参数(其中{{1}当函数声明中只有1个参数时,它将起作用。
知道如何添加多个参数吗?
最终目标是在指定的子目录中找到一个Find函数。
答案 0 :(得分:1)
您需要将-nargs=1
更改为-nargs=+
。这意味着您必须拥有参数但不指定数字。我建议您将Find
函数更改为Find(...)
,如果使用的参数数量无效,请使用a:0
获取错误参数的数量。
具有多个参数的示例函数和命令:
command! -nargs=+ -complete=dir Find call Find(<f-args>)
fun! Find(name, ...)
let dir = getcwd()
if a:0 == 1
let dir = getcwd() . '/' . (a:1 =~ '[/\\]$' ? a:1 : a:1 . '/')
elseif a:0 != 0
echohl ErrorMsg
echo "Must supply 1 or 2 arguments"
echohl NONE
endif
let &efm = "%f"
cexpr []
caddexpr split(glob(dir . '**/*' . escape(a:name, '\`*[]?') . '*'), '\n')
copen
aug Find
au!
au BufLeave <buffer> ccl|aug! Find
aug END
endfun
获取更多帮助
:h :command-nargs
:h ...
修改强> 添加了除@ZyX建议的多个参数之外的函数示例。
答案 1 :(得分:1)
@Peter Rincker的补充答案:如果您想将参数传递给函数,则不应使用"<args>"
,因为已经存在不需要的内置<q-args>
和<f-args>
额外的引用,并没有引入代码注入的可能性(尝试Find ".string(g:)."
与您的代码)。首先将所有参数作为一个项传递,第二个将产生适合函数调用的参数列表:
command -nargs=+ Find :call Find(<f-args>)
需要考虑的另一件事:
shellescape(str, 1)
。你可能会遇到意想不到的问题。strlen(substitute(l:input, "[0-9]", "", "g"))>0
条件相当于input=~#'\D'
,但要大得多。l:
:它是函数内的默认范围。内置glob()
功能:整个system()
行可以替换为
join(map(split(glob('./*'.escape(a:name, '\`*[]?').'*'), "\n"), 'v:key."\t".v:val'), "\n")
execute ":e ".l:line
应该写成execute "e" fnameescape(line)
(这是第三个可以在如此简单的代码片段中注入代码的地方!)。最好在这里使用列表,在这种情况下,您不需要使用某些东西来添加行号:
function s:Find(name)
let list=split(glob('./*'.fnameescape(a:name).'*'), "\n")
if empty(list)
echo string(a:name) "not found"
return
endif
let num=len(list)
if num>1
echo map(copy(list), 'v:key."\t".v:val')
let input=input("Which?")
if empty(input)
return
elseif input=~#'\D'
echo "Not a number"
return
elseif input<1 || input>num
echo "Out of range"
return
endif
let line=list[input]
else
let line=list[0]
endif
execute "e" fnameescape(line)
endfunction
command! -nargs=1 Find :call Find(<f-args)