zsh-users
 help / color / mirror / code / Atom feed
* list last modified files
@ 2015-08-19 21:59 rooom
  2015-08-19 23:16 ` Vin Shelton
  2015-08-20  4:33 ` Bart Schaefer
  0 siblings, 2 replies; 13+ messages in thread
From: rooom @ 2015-08-19 21:59 UTC (permalink / raw)
  To: zsh-users

Hi,

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.

It should work like

lt                              # list 10 recent files from .
lt dir                          # list 10 recent files from dir/
lt dir1 file1 dir2/file* dir3   # list 10 recent files from a given sum of inputs


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}

Here how it should work:
For each argument check if it is directory, and in that case return 10 most recent modified files from that directory, then add other arguments to the list (files) and ignore all non-file arguments (N). After all pass generated set to the 'ls' command which sorts it on modification time and finally pass to 'head -n 10'.


Several question:

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). From my basic understanding no command should be needed to properly handle 'lt file'.

2. I would like to get rid of external `head -n 10` command and use glob qualifiers instead, but as you can see there are already two nested qualifier lists, and I can't see a way to put one more after all of that just to take (om[1,10]).

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...". Note that I cannot remove (N) qualifier because zsh steps in with its own errors - I want zsh to pass argument to 'ls' as it is in case it cannot find files.


Hope it's not too long,
thanks in advance


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-19 21:59 list last modified files rooom
@ 2015-08-19 23:16 ` Vin Shelton
  2015-08-20  4:33 ` Bart Schaefer
  1 sibling, 0 replies; 13+ messages in thread
From: Vin Shelton @ 2015-08-19 23:16 UTC (permalink / raw)
  To: rooom; +Cc: zsh-users

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

Does

 ls -ld *(om[1,10])

do what you want?

  - Vin


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-19 21:59 list last modified files rooom
  2015-08-19 23:16 ` Vin Shelton
@ 2015-08-20  4:33 ` Bart Schaefer
  2015-08-20  5:20   ` Mikael Magnusson
  2015-08-20 13:08   ` rooom
  1 sibling, 2 replies; 13+ messages in thread
From: Bart Schaefer @ 2015-08-20  4:33 UTC (permalink / raw)
  To: zsh-users; +Cc: rooom

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" )
      done
      if [[ -n "${files[1]}" ]]; then
        ls -Adlt -- "${files[@]}"
      else
        ls -Adlt -- *
      fi | head -n 10
    }


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-20  4:33 ` Bart Schaefer
@ 2015-08-20  5:20   ` Mikael Magnusson
  2015-08-20 13:08   ` rooom
  1 sibling, 0 replies; 13+ messages in thread
From: Mikael Magnusson @ 2015-08-20  5:20 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users, rooom

On Thu, Aug 20, 2015 at 6:33 AM, Bart Schaefer
<schaefer@brasslantern.com> wrote:
> 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 --

You can do things like this though
% mkdir ab ac bc dc; () { touch $^@/{foo,bar} } ??
% echo ((ab|b?)/)#foo(e:'(( RANDOM < 16000 ))':)
ab/foo
% echo ((ab|b?)/)#foo(e:'(( RANDOM < 16000 ))':)
bc/foo

You just can't have a / inside a (foo|bar) group, which you do
potentially need sometimes. The pattern I used would also match
ab/bd/ab/bn/foo recursively.

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-20  4:33 ` Bart Schaefer
  2015-08-20  5:20   ` Mikael Magnusson
@ 2015-08-20 13:08   ` rooom
  2015-08-20 13:29     ` Peter Stephenson
  2015-08-23 20:35     ` ZLE: missing edit-buffer widget rooom
  1 sibling, 2 replies; 13+ messages in thread
From: rooom @ 2015-08-20 13:08 UTC (permalink / raw)
  To: zsh-users; +Cc: rooom

Bart Schaefer wrote:

> [...]

Thank you for very detailed explanation!!!

> The ":" doesn't work because you've used it as the delimiter for the
> (e:...:) qualifier.

It's a shame I didn't notice that. It's so obvious now.

> for f; do

And I've learnt on the occasion that 'for' loops over positional
parameters by default,

BTW, what is 'in'?

$ type for in do done
for is a reserved word
in not found
do is a reserved word
done is a reserved word


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-20 13:08   ` rooom
@ 2015-08-20 13:29     ` Peter Stephenson
  2015-08-20 15:34       ` Bart Schaefer
  2015-08-23 20:35     ` ZLE: missing edit-buffer widget rooom
  1 sibling, 1 reply; 13+ messages in thread
From: Peter Stephenson @ 2015-08-20 13:29 UTC (permalink / raw)
  To: zsh-users

On Thu, 20 Aug 2015 15:08:02 +0200
rooom <rooom@prokonto.pl> wrote:
> BTW, what is 'in'?

It's simply a syntactic marker specific to "for" which looks for it in
the list of arguments.  In the original shell syntax it's not
really doing much except make it a bit more readable since you're forced
to use "for var in ...", but zsh makes a virtue of it by allowing you
to do "for var1 var2... in ..." --- unless your variable is called "in",
obviously, in which case you can't.

pws


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-20 13:29     ` Peter Stephenson
@ 2015-08-20 15:34       ` Bart Schaefer
  2015-08-20 15:55         ` Peter Stephenson
       [not found]         ` <20150820165552.2b5ec817__22391.7863135741$1440086232$gmane$org@pwslap01u.europe.root.pri>
  0 siblings, 2 replies; 13+ messages in thread
From: Bart Schaefer @ 2015-08-20 15:34 UTC (permalink / raw)
  To: zsh-users

On Aug 20,  2:29pm, Peter Stephenson wrote:
}
} On Thu, 20 Aug 2015 15:08:02 +0200
} rooom <rooom@prokonto.pl> wrote:
} > BTW, what is 'in'?
} 
} It's simply a syntactic marker specific to "for" which looks for it in
} the list of arguments.  In the original shell syntax it's not
} really doing much except make it a bit more readable since you're forced
} to use "for var in ...", but zsh makes a virtue of it by allowing you
} to do "for var1 var2... in ..." --- unless your variable is called "in",
} obviously, in which case you can't.

Well ...

torch% for in in in; do print $in; done
in
torch% for in in in in in; do print $in; done
in
in
in
torch% unset in
torch% for out in out in out; do print in:$in out:$out; done
in: out:out
in: out:in
in: out:out
torch% for in out in out in; do print $in $out; done
out in
torch%



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-20 15:34       ` Bart Schaefer
@ 2015-08-20 15:55         ` Peter Stephenson
  2015-08-20 17:13           ` Bart Schaefer
       [not found]         ` <20150820165552.2b5ec817__22391.7863135741$1440086232$gmane$org@pwslap01u.europe.root.pri>
  1 sibling, 1 reply; 13+ messages in thread
From: Peter Stephenson @ 2015-08-20 15:55 UTC (permalink / raw)
  To: zsh-users

On Thu, 20 Aug 2015 08:34:31 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Aug 20,  2:29pm, Peter Stephenson wrote:
> }
> } On Thu, 20 Aug 2015 15:08:02 +0200
> } rooom <rooom@prokonto.pl> wrote:
> } > BTW, what is 'in'?
> } 
> } It's simply a syntactic marker specific to "for" which looks for it in
> } the list of arguments.  In the original shell syntax it's not
> } really doing much except make it a bit more readable since you're forced
> } to use "for var in ...", but zsh makes a virtue of it by allowing you
> } to do "for var1 var2... in ..." --- unless your variable is called "in",
> } obviously, in which case you can't.
> 
> Well ...

If you mean you'd rather blind me with code than state the rule...
that worked.

The rule is roughly:
- Assume the first argument is a variable.  This is needed for
backwards compatibility.  Note you can't do:
  vars=(one two three)
  for $vars in 1arg1 2arg1 3arg1 1arg2 2arg2 3arg2; ...
- Otherwise, look for more variables until a word matches "in"
(ignoring crazy variant zsh syntax)
- If no "in", use positional parameters.
- Otherwsie, any words after "in" are the arguments to loop over,
taken in batches of the number of arguments before "in".  The
words are no longer subject to anything more than normal argument
processing.

The rule means this does work:

  set -- 1 2 3 4 5 6
  for one two three; do
    print $one $two $three
  done

1 2 3
4 5 6

and you can replace "one" by "in" if you lke, but don't.

pws


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
       [not found]         ` <20150820165552.2b5ec817__22391.7863135741$1440086232$gmane$org@pwslap01u.europe.root.pri>
@ 2015-08-20 16:42           ` Stephane Chazelas
  2015-08-20 17:09             ` Bart Schaefer
  0 siblings, 1 reply; 13+ messages in thread
From: Stephane Chazelas @ 2015-08-20 16:42 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: zsh-users

2015-08-20 16:55:52 +0100, Peter Stephenson:
[...]
>   set -- 1 2 3 4 5 6
>   for one two three; do
>     print $one $two $three
>   done
> 
> 1 2 3
> 4 5 6
> 
> and you can replace "one" by "in" if you lke, but don't.
[...]

Note that you can also do:

for one two three do
  print $one $two $three
done

Note that "for i do" is Bourne and POSIX while "for i; do" is
neither (though there's talk of making it standard in the next
version of POSIX since most modern shells support it, and
it's a common idiom).

-- 
Stephane


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-20 16:42           ` Stephane Chazelas
@ 2015-08-20 17:09             ` Bart Schaefer
  0 siblings, 0 replies; 13+ messages in thread
From: Bart Schaefer @ 2015-08-20 17:09 UTC (permalink / raw)
  To: Peter Stephenson, Zsh Users

On Thu, Aug 20, 2015 at 9:42 AM, Stephane Chazelas
<stephane.chazelas@gmail.com> wrote:
>
> Note that you can also do:
>
> for one two three do
>   print $one $two $three
> done

Or even:

for do do print $do; done

But as Peter said, don't.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: list last modified files
  2015-08-20 15:55         ` Peter Stephenson
@ 2015-08-20 17:13           ` Bart Schaefer
  0 siblings, 0 replies; 13+ messages in thread
From: Bart Schaefer @ 2015-08-20 17:13 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh Users

On Thu, Aug 20, 2015 at 8:55 AM, Peter Stephenson
<p.stephenson@samsung.com> wrote:
>
> If you mean you'd rather blind me with code than state the rule...
> that worked.

I wasn't expecting any response at all other than perhaps some
amusement, but as long as we're stating rules:

> - If no "in", use positional parameters.
> - Otherwsie, any words after "in" are the arguments to loop over,
> taken in batches of the number of arguments before "in".  The
> words are no longer subject to anything more than normal argument
> processing.

Except that "do" is a keyword, so it ends normal argument processing.
(This is the point of Stephane's follow-up.)


^ permalink raw reply	[flat|nested] 13+ messages in thread

* ZLE: missing edit-buffer widget
  2015-08-20 13:08   ` rooom
  2015-08-20 13:29     ` Peter Stephenson
@ 2015-08-23 20:35     ` rooom
  2015-08-23 21:45       ` Bart Schaefer
  1 sibling, 1 reply; 13+ messages in thread
From: rooom @ 2015-08-23 20:35 UTC (permalink / raw)
  To: zsh-users

Hi,

Sometimes I want to edit multi line command with <up>/<down> arrows
and in these cases push-line-or-edit widget is very helpful.
However if activated for single line command it is equivalent to push-line 
(according to manual), so that I have to hit additional enter to get
the line back.

I'm looking for widget "edit-line" or "edit-buffer" which move entire
command line into editor buffer regardless if it is single line or 
multi-line construct.

For some reason the simple definition doesn't work:

push-input-get-line () {
    zle .push-input
    zle .get-line
}
zle -N push-input-get-line

Any suggestion?


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: ZLE: missing edit-buffer widget
  2015-08-23 20:35     ` ZLE: missing edit-buffer widget rooom
@ 2015-08-23 21:45       ` Bart Schaefer
  0 siblings, 0 replies; 13+ messages in thread
From: Bart Schaefer @ 2015-08-23 21:45 UTC (permalink / raw)
  To: zsh-users; +Cc: rooom

On Aug 23, 10:35pm, rooom wrote:
}
} I'm looking for widget "edit-line" or "edit-buffer" which move entire
} command line into editor buffer regardless if it is single line or 
} multi-line construct.

Look at the edit-command-line function in the distribution (probably
findable with   print -R $^fpath/edit-command-line(N)  )

You want to do what that does, except skip invoking the editor on a
file.  So something like:

    print -Rz - "$PREBUFFER$BUFFER"
    zle send-break

This is exactly what push-line-or-edit does except that if there is
only one line of input it skips the send-break.  (push-line-or-edit
was implemented before user-defined widgets were available.)

You could also write a little wrapper that's just a no-op if there
is only one line:

    edit-if-multi-line () {
      if [[ -n "$PREBUFFER" ]]; then
	zle .push-line-or-edit
      fi
      return 0
    }

} For some reason the simple definition doesn't work:
} 
} push-input-get-line () {
}     zle .push-input
}     zle .get-line
} }

You've discovered the reason push-line-or-edit exists.  In a multi-
line construct, the stuff entered at previous prompts (the value of
$PREBUFFER) has already passed through both the editor and the shell
parser and is only awaiting the completion of the parse to be run as
shell input.  In order to get it back and undo the parser state,
push-input has to abort the whole parser loop, so get-line never has
a chance to be executed.


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2015-08-23 21:45 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-19 21:59 list last modified files rooom
2015-08-19 23:16 ` Vin Shelton
2015-08-20  4:33 ` Bart Schaefer
2015-08-20  5:20   ` Mikael Magnusson
2015-08-20 13:08   ` rooom
2015-08-20 13:29     ` Peter Stephenson
2015-08-20 15:34       ` Bart Schaefer
2015-08-20 15:55         ` Peter Stephenson
2015-08-20 17:13           ` Bart Schaefer
     [not found]         ` <20150820165552.2b5ec817__22391.7863135741$1440086232$gmane$org@pwslap01u.europe.root.pri>
2015-08-20 16:42           ` Stephane Chazelas
2015-08-20 17:09             ` Bart Schaefer
2015-08-23 20:35     ` ZLE: missing edit-buffer widget rooom
2015-08-23 21:45       ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).