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

Re: list last modified files

On Aug 19, 11:59pm, rooom wrote:
} I'm trying to write a function which lists 10 most recent files
} from given sets (not only directories), something like trivial
} `alias lt=ls -lat | head -n 10`, but better.

You can't glob if this is an arbibrary list of files. A glob qualifier
can only be applied to files within a single directory (unless you can
use a recursive glob like **/*) because you can't mix directory
traversal with grouping, that is, (dir1/*|dir3/*) is an error.  So you
have to do one of --

(a) get all the file names+times and sort them yourself, then pass
    the first ten of them to "ls -lat"
(b) get all the file names and let "ls -lat" sort them, then filter
    out all but the first ten

Whichever of these you do, you have to filter all the files down to
the first ten.  You can do this inside zsh with e.g. an array slice
or outside with "head" as you already did.

However, you've thrown in this additional constraint:

} 3. When I run it with only non-existing files like 'lt nonexistingfile'
} then it prints single dot '.'. I would prefer to print error from 'ls'
} command like "ls: cannot access...".

That means you need the "nonomatch" option set, and you need to pass
all the files (whether they exist or not) to "ls" so that it can
generate the error messages.  That pretty much eliminates option (a)
[or makes it too messy to bother with], and since you're already using
"ls" for the sorting, there's no benefit to avoiding "head".

You're also probably better off testing the no-argument case separately
rather than attempting to use ${@:-.}, because to get the behavior you
describe in (3) you never really want to pass "./file" to "ls".

Note that unless you want to "noglob" the whole thing or use nonomatch
eveyrwhere, you won't be able to avoid having zsh print the error message
when a pattern on the command line fails to match any files.

} Here is my solution so far, which I think is overcomplicated and iffy:
} lt() {ls -Adlt -- "${^@:-.}"(Ne:'[[ -d $REPLY ]] && reply=($REPLY/*(DNomon[1,10])) || true':) | head -n 10}

This is not far wrong, aside from the remark above about not passing
"./" to "ls", but unless you think you're going to generate too many
files for "ls -l" or that it will take too long, there's no reason to
do the [1,10] in the glob qualifier.  On the other hand, we've already
established that you can't use a glob qualifier.

} 1. First of all I cannot understand why do I need a command "true" to
} list properly arguments which are not directories. I can put there
} other command as well like 'echo >/dev/null', but 'true' is simplest I
} could find (for example ':' doesn't work).

The ":" doesn't work because you've used it as the delimiter for the
(e:...:) qualifier.  It's a little unintuitive, but due to order of
parsing the quotes have been removed before the qualifier gets a
chance to start looking for the matching colons.

To explain why you need SOMETHING there:  When [[ -d $REPLY ]] is not
true, the entire && expression becomes false, and (e:false:) means to
leave the file name out of the result.  It might be more obvious to
say you should have written:

    e:'[[ -d $REPLY ]] && reply=($REPLY/*) || reply=($REPLY)':

Anyway here is my suggested function given all your constraints:

    lt() {
      setopt localoptions nonomatch
      local f
      local -a files
      for f; do
	[[ -d "$f" ]] && files+=( "$f"/* ) || files+=( "$f" )
      if [[ -n "${files[1]}" ]]; then
        ls -Adlt -- "${files[@]}"
        ls -Adlt -- *
      fi | head -n 10

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