Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Emulating 'locate'



On Oct 4,  9:48am, Lloyd Zusman wrote:
}
} >> I'm not sure how it compares to this:
} >>   locate() { find / -name "*${^*}*" -print }
} 
} zsh interprets the ${^*} part in intersperses it between the other two
} asterisks when the shell function is being invoked, and 'find'
} interprets the result.  I think I should have left out the ^, however,
} or probably only used ${1}.

Actually the caret is meaningless for $* when in double quotes.  What
you were thinking of was probably "*${^@}*".

    set "*${^@}*"
    eval find / '\(' -name "'${(j:' -o -name ':)@}'" '\)' -print

} I just ran a timing test, and unfortunately, 'find' fares better than
} your locate function, which I named 'xlocate' on my system.  Here are
} the results:
} 
}   find / -name specific-file -print   # 15 min 19 sec elapsed
}   xlocate specific-file               # 28 min 40 sec elapsed

I think that's expected rather than unfortunate.  For one thing, that
find command will only print paths that end in names matching the
pattern, whereas xlocate descends and prints entire trees below any
directory matching the pattern.  (I'm not even sure how to express the
latter in find without resorting to -exec of another find.)  However,
zsh also does a lot of stat() calls during the glob to avoid following
symlinks and perform MARK_DIRS and so on, and it buffers up all the
results and does a duplicate-eliminating sort pass as well (in case of
a path like x/y/z/foo/a/b/c/foo/p/d/q when globbing for **/foo/*).

} Well, I think that there is a way to make it quite good for everyday use
} without having to go so far as to create a database: just come up with a
} way to target the search from a specific directory [...]
} 
} Here's my first try at it (I call it 'xlocate' so as not to conflict
} with the 'locate' command on my system):
} 
}   xlocate() {
}     setopt nullglob extendedglob
}     eval print -l ${argv[1]%/}'/**/'${^argv[2,-1]}'{,/**/*}'
}   }

You should use localoptions there, and you can avoid the eval:

    xlocate() {
	setopt localoptions nullglob extendedglob
	print -l ${~argv[1]%/}/**/${~^argv[2,-1]}{,/**/*}
    }

And if you add this alias (which must come after the function def'n):

    alias xlocate='noglob xlocate'

Then you can use glob patterns without quoting, as in

    xlocate ~ *.c

} I removed the asterisks before and after the ${^argv[2,-1]} so I don't
} lose the ability to do the following:
} 
}   xlocate ~ '*.c'   # only matches *.c files under HOME
}   xlocate ~ c       # only matches files named 'c' under HOME

You could have the best of both worlds with a simple addition:

    xlocate() {
	setopt localoptions nullglob extendedglob
	# If there's only one argument, behave more like 'locate'
	((ARGC == 1)) && set / "*${(q)1}*"
	print -l ${~argv[1]%/}/**/${~^argv[2,-1]}{,/**/*}
    }

Or maybe this is better:

    xlocate() {
	setopt localoptions nullglob extendedglob
	# If the first argument is not a directory, behave like locate
	[ -d $~1 ] || set / "*${^@}*"
	print -l ${~argv[1]%/}/**/${~^argv[2,-1]}{,/**/*}
    }

(I used [ ] there for a reason: [[ ]] doesn't glob the argument of -d.)



Messages sorted by: Reverse Date, Date, Thread, Author