I have a nice one-liner that I want to create an alias of. An instance of that one-liner looks like the following:
less +G $(find /var/logs -name 'service-output.root*' -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d' ')
Here it looks into the /var/logs/ directory, does prefix matching for all files starting with service-output.root and opens the latest with less.
The alias I want to create should get the directory and the prefix regex as arguments, thus I came up with the following alias function
function l-log {
if [[ ! ${#} -eq 2 ]] || [[ ! -d ${1} ]]; then
echo "Usage: ${0} <dir> <regex>"
return 1
fi
local DIR=${1}
local FILE_REGEX=${2}
find ${DIR} -name '${FILE_REGEX}' -printf '%T@ %p\\n' | sort -n | tail -1 | cut -f2- -d' '
}
The problem is that this does not work.
$ l-log /var/logs/ service-output.root*
zsh: no matches found: service-output.root*
I have enabled function debugging
$ functions -t l-log
and tinkering a bit it seems that the problem is about variable ${FILE_REGEX} not being properly expanded because it is single quoted (and it has to be single quoted)
$ l-log /apollo/var/logs/ apollo-update.root 1 ↵
+l-log:1> [[ ! 2 -eq 2 ]]
+l-log:1> [[ ! -d /apollo/var/logs/ ]]
+l-log:5> local DIR=/apollo/var/logs/
+l-log:6> local FILE_REGEX=apollo-update.root
+l-log:13> find /apollo/var/logs/ -name '${FILE_REGEX}' -printf '%T@ %p\\n'
+l-log:13> sort -n
+l-log:13> tail -1
+l-log:13> cut -f2- '-d '
I have tried several things like double quoting the variables, escaping single and double quotes, using double double (!) quotes (""${FILE_REGEX}"") and back ticking (`) but I haven't managed to get +l-log:13> above to become find /apollo/var/logs/ -name 'service-output.root*' -printf '%T@ %p\\n'
Any help would be great!
There are a few things happening here.
zshwill try to expand / glob the wildcard before it even gets to the function - that's where thezsh: no matches foundmessage is coming from. Some ways to call the function:l-log /var/logs/ 'service-output.root*'l-log /var/logs/ service-output.root\*noglob l-log /var/logs/ service-output.root*find. Use${FILE_REGEX}.The last part can be a bit confusing, since wildcards usually need to be quoted when calling
find. But here the wildcard is contained in a variable, sozshis not going to do any globbing unless you specifically ask for that. The single quotes are preventing the variable$FILE_REGEXfrom being expanded, and you need that to occur.BTW, you can do this kind of operation without
find, using justzsh. Here's one option:Some notes on the parts:
pattern=${1:?}/**/${2:?}(om[1])- this builds a glob pattern that we'll use in the next operation..../**/...- uses the recursive glob operator**/to traverse the directory tree, similar tofind.${1:?}- substitutes the directory name supplied by the caller as the first parameter. With the${ :?}expansion, this will print an error message and exit the function if the parameter is empty.${2:?}- substitutes the second parameter, the filename wildcard pattern.(om)- a glob qualifier. This asks the shell to sort(o)the results by modification time(m), with the most recent file listed first.([1])- another glob qualifier that selects which items to include. In this case the result will have at most one filename; with the(om)qualifier it will be the most recent matching file.less -- ${~pattern}- expands the pattern and callsless.${~pattern}- expands to the contents of thepatternvariable. Since it is a${~...}expansion, the shell then performs globbing with the resulting string, and substitutes the single filename that matches the pattern (if one exists).less -- <filename>- callslessto display the file. The--is a guard against filenames that may start with-.zshallows expansions to be nested, so the whole thing could be one slightly cryptic line:less -- ${~:-${1:?}/**/${2:?}(om[1])}.This function can now be used to search for a file, but the wildcard will need to quoted or escaped as described above, e.g.:
To avoid quoting patterns for this command, we can use an
aliasto always add thenoglobprecommand modifier:Calling it with the alias: