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