Vim Tips Wiki
Explore
Main Page
All Pages
Community
Interactive Maps
Community portal
To do
FANDOM
Fan Central
BETA
Games
Anime
Movies
TV
Video
Wikis
Explore Wikis
Community Central
Start a Wiki
Don't have an account?
Register
Sign In
Sign In
Register
Vim Tips Wiki
1,649
pages
Explore
Main Page
All Pages
Community
Interactive Maps
Community portal
To do
Editing
Find files in subdirectories
Back to page
Edit
Edit source
View history
Talk (0)
Edit Page
Find files in subdirectories
We recommend that you
log in
before editing. This will allow other users to leave you a message about your edit, and will let you track edits via your
Watchlist
.
Creating an account
is quick and free.
The edit appears to have already been undone.
Anti-spam check. Do
not
fill this in!
{{Deprecated|Version 7.3 of Vim supports tab-completion of <code>:find</code> command, this is easier than scripting and should be prominently mentioned.}} {{TipImported |id=1234 |previous=1232 |next=1235 |created=2006 |complexity=intermediate |author=Vladimir Marek |version=6.0 |rating=10/4 |category1= |category2= }} I'm working with big, nested workspaces and often I don't remember the exact path to the file, only its filename or part of the filename. If I know some of the text ''in'' the file, I could always [[Find in files within Vim#Recursive Search|recursively use 'vimgrep']], but for searching on filenames alone I have been using: <pre> :!find . -name ... </pre> to locate the file and then edit it. I was trying to find if there is some solution directly in Vim, and haven't found one. Closest were <code>:find</code> and <code>:globpath()</code>. <code>:find</code> works nearly as I need, but unfortunatelly it opens the first file of a given name without telling me that there are more. For <code>globpath()</code> I was unable to make it work with the '**' construction, so that it would look into all subdirectories under current directory. So I wrote this small function. You can use it like this: <pre> :Find whatever.c - this opens the file "src/core/whatever.c" </pre> If there is more than one match, it will present you a selection: <pre> :Find Makefile 1 ./src/Makefile 2 ./src/core/Makefile 3 ./src/api/Makefile ... 89 ./src/deelply/hidden/Makefile 90 ./Makefile Which ? (CR=nothing) </pre> You may also use wildchars (whatever find(1) knows). <pre> :Find *stream*.c 1 ./src/core/i_stream.c 2 ./src/core/o_stream.c 3 ./src/core/streamio.c Which ? (CR=nothing) </pre> The function itself: <pre> " Find file in current directory and edit it. function! Find(name) let l:list=system("find . -name '".a:name."' | perl -ne 'print \"$.\\t$_\"'") " replace above line with below one for gvim on windows " let l:list=system("find . -name ".a:name." | perl -ne \"print qq{$.\\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>") </pre> ==Related Plugins== *{{script|id=4426|text=find-complete: Finds files on path with tab to auto-complete filename functionality}} ==Comments== You can load an arbitrary list of files with :args <pattern>, for instance: Open all .c or .h files in the directory (and it's subdirectories) two directories up from the current directory: :args ../../**/*.[ch] The only caveat (and it's a major one) is that it's very slow. There's also the <code>:Explore **/[pattern]</code> command, available via netrw.vim. Example: <pre> :Explore **/*.vim </pre> This command will show the current match number out of the total quantity of matches in the status line, and one may move forwards and backwards in the matching files list. Hitting the <CR> on a matching file opens the file, of course. ---- When I try the <code>:Explore</code> command it gives error: <code>E77: Too many file names</code> Adding a quit button is not a bad idea. Vim internal pager has quit bound to 'q' (see {{help|more-prompt}}), but this will stop my original script. I tried to use try-catch block around and it seems to work. <pre> if l:num != 1 try echo l:list catch /.*/ endtry let l:input=input("Which ? (CR=nothing)\n") </pre> ---- I think, it will be better to use vimgrep for find, than it works also under windows. I don't know how. Here is a version the exclude subversion directory: <pre> function! Find(name) let l:list=system("find . -name '".a:name."' | grep -v \".svn/\" | 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>") </pre> ---- Here is a other nice solution to vimrc <pre> if exists("$PROJECTDIR") set path=$PROJECTDIR/** set tags=$PROJECTDIR/tags endif </pre> well, is on the shell $PROJECTDIR defined the include recursive all directories. with <code>:find foo.cpp</code> it's open the file on Vim. ---- Yes, I have been using construct very similar to yours, the trouble was that :find opens first file of a given name it finds, without telling you that there are other two. The other thing, :find also does not like wildcard characters, they are expanded before the :find is executed. I would like to be able to use ':find blah*.c'. Or maybe I just don't know how to use :find properly ? :) ---- The tags mechanism is how I jump around files. The Exuberant Ctags tool allows you to tag the files themselves. I have a script that looks like this: <pre> ctags --extra=+f --langdef=file --langmap='file:.xml.jsp.properties.xslt' \ --exclude=WEB-INF/classes \ -R src/com src/java testharness/src web/pages sql gen WEB-INF </pre> In your Vim session set the tags file location <pre> :set tags=pathtotags/tags </pre> And then use :tag FileName Added bonus, :tag lets you use tab completion! ---- >> But maybe there is some way to generate two different tag files and use them in Vim at the same time? Sure there is! You can try: <pre> set tags=<path1>/tags,<path2>,tags </pre> ---- Thank you, very useful script. Would it be possible to add an option to open the found file with another program? For example openoffice in case the found file is odt? ---- If the user has no permission for some of the files, they are included in the listing. I solved it by redirecting stderr to /dev/null. Old line: <pre> let l:list=system("find . -name '".a:name."' | grep -v \".svn/\" | perl -ne 'print \"$.\\t$_\"'") </pre> New line: <pre> let l:list=system("find . -name '".a:name."' 2> /dev/null | grep -v \".svn/\" | perl -ne 'print \"$.\\t$_\"'") </pre> ---- The find command can be nicely integrated with the quickfix functionality of vim. Here an implementation based on the python vim module. Put this in you .vimrc file: <pre> python << EOL import vim def Finder(*args): ''' Operation is as follows: - request the starting dir - find command finds the requested file - the printf param formats it to accomodate the errorformat '%f:%l:%m' - line is always 1, message is empty (-) - the 'cgete' vim command puts it into the quickfix errorlist - 'botright copen' opens the quickfix list ''' start_dir = vim.eval('input("Start from dir: ", getcwd(), "dir")') find_cmd = (r'find %s -name %s -printf %%p:1:-\\n' % (start_dir, args[0])) vim.command("cgete system('%s')" % find_cmd) vim.command('botright copen') EOL command! -nargs=1 Find :py Finder("<args>") </pre> ------ This is the slightly modified version of the script so it accepts an optional (second) argument. If given it represents a path to be used in a search. The result of the search is redirected to a quickfix window. The quickfix window gets focus after the search is done. <pre> " Find file in current directory and edit it. function! Find(...) let path="." if a:0==2 let path=a:2 endif let l:list=system("find ".path. " -name '".a:1."' | grep -v .svn ") let l:num=strlen(substitute(l:list, "[^\n]", "", "g")) if l:num < 1 echo "'".a:1."' not found" return endif if l:num != 1 let tmpfile = tempname() exe "redir! > " . tmpfile silent echon l:list redir END let old_efm = &efm set efm=%f if exists(":cgetfile") execute "silent! cgetfile " . tmpfile else execute "silent! cfile " . tmpfile endif let &efm = old_efm " Open the quickfix window below the current window botright copen call delete(tmpfile) endif endfunction command! -nargs=* Find :call Find(<f-args>) </pre> ------ One more slight modification. If only one file is found, the script directly above this comment will not open anything. And since there's no need to open the quickfix list for one file, it just opens it directly (like find). <pre> " Find file in current directory and edit it. function! Find(...) let path="." if a:0==2 let path=a:2 endif let l:list=system("find ".path. " -name '".a:1."' | grep -v .svn ") let l:num=strlen(substitute(l:list, "[^\n]", "", "g")) if l:num < 1 echo "'".a:1."' not found" return endif if l:num == 1 exe "open " . substitute(l:list, "\n", "", "g") else let tmpfile = tempname() exe "redir! > " . tmpfile silent echon l:list redir END let old_efm = &efm set efm=%f if exists(":cgetfile") execute "silent! cgetfile " . tmpfile else execute "silent! cfile " . tmpfile endif let &efm = old_efm " Open the quickfix window below the current window botright copen call delete(tmpfile) endif endfunction command! -nargs=* Find :call Find(<f-args>) </pre> ----- Added configurable inverted grep. Set g:FindIgnore in your .vimrc to a list of strings: <pre> set g:FindIgnore = ['.swp', '.pyc', '.class', '.git', '.svn'] </pre> <pre> " Find file in current directory and edit it. function! Find(...) if a:0==2 let path=a:1 let query=a:2 else let path="./" let query=a:1 endif if !exists("g:FindIgnore") let ignore = "" else let ignore = " | egrep -v '".join(g:FindIgnore, "|")."'" endif let l:list=system("find ".path." -type f -iname '*".query."*'".ignore) let l:num=strlen(substitute(l:list, "[^\n]", "", "g")) if l:num < 1 echo "'".query."' not found" return endif if l:num == 1 exe "open " . substitute(l:list, "\n", "", "g") else let tmpfile = tempname() exe "redir! > " . tmpfile silent echon l:list redir END let old_efm = &efm set efm=%f if exists(":cgetfile") execute "silent! cgetfile " . tmpfile else execute "silent! cfile " . tmpfile endif let &efm = old_efm " Open the quickfix window below the current window botright copen call delete(tmpfile) endif endfunction command! -nargs=* Find :call Find(<f-args></pre> ---- Modified above function to use the -path parameter instead of -iname, this allows things like: <pre> :Find 'log*htt*err*' /var </pre> Which would find /var/log/httpd/error_log <pre> Find 'pub*.tpl' </pre> Which would find all .tpl files in public_html <pre> function! Find(...) if a:0==2 let path=a:1 let query=a:2 else let path="./" let query=a:1 endif if !exists("g:FindIgnore") let ignore = "" else let ignore = " | egrep -v '".join(g:FindIgnore, "|")."'" endif let l:list=system("find ".path." -type f -path '".query."'".ignore) let l:num=strlen(substitute(l:list, "[^\n]", "", "g")) if l:num < 1 echo "'".query."' not found" return endif if l:num == 1 exe "open " . substitute(l:list, "\n", "", "g") else let tmpfile = tempname() exe "redir! > " . tmpfile silent echon l:list redir END let old_efm = &efm set efm=%f if exists(":cgetfile") execute "silent! cgetfile " . tmpfile else execute "silent! cfile " . tmpfile endif let &efm = old_efm " Open the quickfix window below the current window botright copen call delete(tmpfile) endif endfunction command! -nargs=* Find :call Find(<f-args>) </pre>
Summary:
Please note that all contributions to the Vim Tips Wiki are considered to be released under the CC-BY-SA
Cancel
Editing help
(opens in new window)
Templates used on this page:
Template:Deprecated
(
view source
)
Template:Help
(
view source
)
Template:Navigation
(
view source
)
Template:Script
(
view source
)
Template:TipImported
(
view source
)
Follow on IG
TikTok
Join Fan Lab