zshzsh-alias

Expanding/resolving variable inside single quotes in zsh alias function


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!


Solution

  • There are a few things happening here.

    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, so zsh is not going to do any globbing unless you specifically ask for that. The single quotes are preventing the variable $FILE_REGEX from being expanded, and you need that to occur.


    BTW, you can do this kind of operation without find, using just zsh. Here's one option:

    #!/usr/bin/env zsh
    llogBase() {
      local pattern=${1:?}/**/${2:?}(om[1])
      less -- ${~pattern}
    }
    

    Some notes on the parts:

    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.:

    llogBase /var/logs 'service-output.root*'
    

    To avoid quoting patterns for this command, we can use an alias to always add the noglob precommand modifier:

    alias llog='noglob llogBase'
    

    Calling it with the alias:

    llog /var/logs service-output.root*