grep allow multiple filespecs of course, and if there are spaces they have to be quoted naturally: $ grep 'some string' filename 'filename with spaces' more_files* My wrapper around grep has a problem with that tho because when I'm grabbing the filespecs if I do something like this: while [[ -n "$1" ]]; do ffilespec+=" $1" shift done Obviously the final list of files is chaos once the original enclosing single quotes are stripped off as they are. Trying: while [[ -n "'$1'" ]]; do ... as a brute force addition of single quotes works fine with filenames with spaces but it also kills any glob expansions. Can I have it both ways? I think I need to preserve any single quotes I add on CL verbatim rather than trying to add them myself in the code. The various (q) family flags don't seem to work. The closest I can get is to both single quote and backslash the spaces as the input to my wrapper: g 'some string' filename 'filename\ with\ spaces' more_files* ... and that's not so bad, but I'm betting there's an elegant way. Hafta somehow not break the middle filespec on those spaces even without the backslashes. And preserve the single quotes. Basically the arguments should pass thru the function absolutely unmolested and grep should get them exactly as I type them.
> On Feb 9, 2021, at 3:42 PM, Ray Andrews <rayandrews@eastlink.ca> wrote: > > grep allow multiple filespecs of course, and if there are spaces they have to be quoted naturally: > > $ grep 'some string' filename 'filename with spaces' more_files* > > My wrapper around grep has a problem with that tho because when I'm grabbing the filespecs if I do something like this: > > while [[ -n "$1" ]]; do > ffilespec+=" $1" > shift > done What does your wrapper do that requires accreting a scalar like this? Simply running grep $@ would work without issue. > Obviously the final list of files is chaos once the original enclosing single quotes are stripped off as they are. Trying: > > while [[ -n "'$1'" ]]; do > > ... as a brute force addition of single quotes works fine with filenames with spaces but it also kills any glob expansions. This conditional will always return 0. I assume your loop runs 'break' at some point. vq
On Tue, 2021-02-09 at 12:42 -0800, Ray Andrews wrote:
> grep allow multiple filespecs of course, and if there are spaces they
> have to be quoted naturally:
>
> $ grep 'some string' filename 'filename with spaces' more_files*
>
> My wrapper around grep has a problem with that tho because when I'm
> grabbing the filespecs if I do something like this:
>
> while [[ -n "$1" ]]; do
> ffilespec+=" $1"
> shift
> done
This is what arrays are for.
unsetopt shwordsplit # leave poor unsuspecting spaces alone
filespec=()
while [[ -n $1 ]]; do
filespec+=($1)
shift
done
In this case, actually, all you're doing is the equivalent of
a single array assignment.
filespec=("$@")
pws
On 2021-02-09 1:08 p.m., Peter Stephenson wrote:
>
>> In this case, actually, all you're doing is the equivalent of
>> a single array assignment.
>>
>> filespec=("$@")
>>
>> pws
It doesn't seem to solve the problem. Here's a real example:
$ g ,H 'execute' 'g,46,w4 now default' f
... The search string is 'execute' and the two target files follow. But
after zsh is finished stripping of the quotes the final grep command
looks like this:
GREP_COLOR='01;33' grep --color=always -iFIHn -d skip -- 'execute'
g,46,w4 now default f
... and of course it won't work due to the spaces. Input via an array
seems to make no difference. (Unless I'm doing it wrong.) If I do this:
$ g ,H 'execute' 'g,46,w4\ now\ default' f
... I get:
GREP_COLOR='01;33' grep --color=always -iFIHn -d skip -- 'execute'
g,46,w4\ now\ default f
... which works fine. Finally,:
$ g ,H 'execute' g,46,w4\ now\ default f
... gives:
GREP_COLOR='01;33' grep --color=always -iFIHn -d skip -- 'execute'
g,46,w4 now default f
... and again the unquoted spaces screw it up. Is there any way to
force the single quotes to pass through as literals?
Lawrence:
It's all rather complicated, my wrappers do all sorts of mischief before
calling grep.
BTW Peter I'm sure enjoying your User's Guide. It should fortify me
enough to finally read the manual.
> On Feb 9, 2021, at 6:45 PM, Ray Andrews <rayandrews@eastlink.ca> wrote: > > On 2021-02-09 1:08 p.m., Peter Stephenson wrote: >> >>> In this case, actually, all you're doing is the equivalent of >>> a single array assignment. >>> >>> filespec=("$@") >>> >>> pws > It doesn't seem to solve the problem. Here's a real example: > > $ g ,H 'execute' 'g,46,w4 now default' f > ... The search string is 'execute' and the two target files follow. But after zsh is finished stripping of the quotes the final grep command looks like this: > > GREP_COLOR='01;33' grep --color=always -iFIHn -d skip -- 'execute' g,46,w4 now default f > > ... and of course it won't work due to the spaces. Input via an array seems to make no difference. (Unless I'm doing it wrong.) If I do this: > > $ g ,H 'execute' 'g,46,w4\ now\ default' f > ... I get: > > GREP_COLOR='01;33' grep --color=always -iFIHn -d skip -- 'execute' g,46,w4\ now\ default f > ... which works fine. Finally,: > > $ g ,H 'execute' g,46,w4\ now\ default f > ... gives: > > GREP_COLOR='01;33' grep --color=always -iFIHn -d skip -- 'execute' g,46,w4 now default f > ... and again the unquoted spaces screw it up. Is there any way to force the single quotes to pass through as literals? How are you assembling the grep command? How are you executing it? > Lawrence: > > It's all rather complicated, my wrappers do all sorts of mischief before calling grep. For the less clairvoyant of us, your refusal to reveal anything but the smallest slice of your actual code makes it rather difficult to help. vq
On 2021-02-09 4:22 p.m., Lawrence Velázquez wrote:
> For the less clairvoyant of us, your refusal to reveal anything but
> the smallest slice of your actual code makes it rather difficult
> to help.
>
I'm trying to keep is as simple as possible because the totality of the
thing doesn't
matter, just the specific problem. When a function argument will end up
making
an argument to grep, and the argument to grep must have single quotes,
but the single
quotes typed in the function argument are stripped off. What's the
solution? grep must
have " $ grep 'file name' " with intact quotes but zsh always strips
them off. There
must be a solution. Or perhaps I'm stuck with " $ grep 'file\ name' --
seems the single
quotes preserve the backlash which in turn forces 'file name' to be a
single word as
wanted. It's ok, but I wonder if there's a better way. I thought the
(q) flag might
do it. Memory tickles that I learned how to do this once.
Hey, just fiddling around with it right now and:
$ g ,H 'execute' "'g,46,w4 now default'" f
... double quote the single quotes and the single quotes remain. Logical
too, outer
quotes will be stripped, so just throw in another set. Final grep is:
GREP_COLOR='01;33' grep --color=always -iFIHn -d skip -- 'execute'
'g,46,w4 now default' f
... all good.
> On Feb 9, 2021, at 7:51 PM, Ray Andrews <rayandrews@eastlink.ca> wrote: > > On 2021-02-09 4:22 p.m., Lawrence Velázquez wrote: >> For the less clairvoyant of us, your refusal to reveal anything but >> the smallest slice of your actual code makes it rather difficult >> to help. >> > I'm trying to keep is as simple as possible because the totality of the thing doesn't > matter, just the specific problem. Imagine developing stomach pain, self-diagnosing an ulcer, and refusing to answer doctors' questions about medication, diet, existing conditions, or anything not ulcer-related because you're "trying to keep it as simple as possible because the totality of the thing doesn't matter, just the specific problem", which you have already decided is an ulcer and cannot possibly be anything other than an ulcer. > When a function argument will end up making > an argument to grep, and the argument to grep must have single quotes, but the single > quotes typed in the function argument are stripped off. What's the solution? grep must > have " $ grep 'file name' " with intact quotes but zsh always strips them off. grep does not need those arguments to have single quotes. It needs them to remain a single word, which is related but not identical. > There > must be a solution. Or perhaps I'm stuck with " $ grep 'file\ name' -- seems the single > quotes preserve the backlash which in turn forces 'file name' to be a single word as > wanted. It's ok, but I wonder if there's a better way. I thought the (q) flag might > do it. Memory tickles that I learned how to do this once. > > Hey, just fiddling around with it right now and: > > $ g ,H 'execute' "'g,46,w4 now default'" f > ... double quote the single quotes and the single quotes remain. Logical too, outer > quotes will be stripped, so just throw in another set. Final grep is: > > GREP_COLOR='01;33' grep --color=always -iFIHn -d skip -- 'execute' 'g,46,w4 now default' f > ... all good. It sure sounds like you're assembling a scalar and eval-ing it, but who's to say. (q) works fine in this toy example. % () { eval "print -rC1 -- $@" } a 'b c' d 'e f g' a b c d e f g % () { eval "print -rC1 -- ${(q)@}" } a 'b c' d 'e f g' a b c d e f g -- vq
>>>>> On February 9, 2021 Ray Andrews <rayandrews@eastlink.ca> wrote: > When a function argument will end up making > an argument to grep, and the argument to grep must have single quotes, but the > single > quotes typed in the function argument are stripped off. What's the solution? You've gone wrong thinking 'the argument to grep must have single quotes'. You need single quotes to get the filename into the function/script as a single argument initially, but if you keep it in an array value, you can then expand the array in the grep invocation preserving the arguments using the "${array[@]}" syntax. consider grep_wrapper () { grepargs=() while [[ $# -gt 0 ]] ; do arg="$1" shift case $arg in --foo* ) echo "do something else with $arg" ;; * ) grepargs+=( "$arg" ) ;; esac done grep --color=always -i -- "${grepargs[@]}" } based on 'unsetopt shwordsplit' and some other options, you can get away without some of the double quoting and {}. but if you want to be able to preserve the empty string, "", as an argument you are pretty much stuck needing double quotes. Greg
On 2021-02-09 5:46 p.m., Lawrence Velázquez wrote: > ... which youhave already decided is an ulcer and cannot possibly be > anything > other than an ulcer. It would take far too long to explain the whole show. If you have the time and interest contact me privately and I'll show you what I'm up to. But I already waste far too much of the list's time. > It sure sounds like you're assembling a scalar and eval-ing it, That's exactly what I'm doing. But far more. > % () { eval "print -rC1 -- ${(q)@}" } a 'b c' d 'e f g' > a > b c > d > e f g > > Thing is that if those args were going to grep and they were filenames you'd end up with: $ grep [some search string] a b c d e f g ... whereas your filenames are 'a' 'b c' d and 'e f g'. The single quotes must be preserved or the spaces backslashed. But as I just posted, double quoting the whole filespec: "a 'b c' d 'e f g' " ... appears to be working fine. grep looks in four files not seven. Seems the double quotes preserve the single quotes.
On 2021-02-09 5:52 p.m., Greg Klanderman wrote:
> grep --color=always -i -- "${grepargs[@]}"
I'll look at that all when I have time Greg but it sure looks like it's
gonna work. Thanks.
> On Feb 9, 2021, at 9:25 PM, Ray Andrews <rayandrews@eastlink.ca> wrote: > > On 2021-02-09 5:46 p.m., Lawrence Velázquez wrote: > >> ... which youhave already decided is an ulcer and cannot possibly be anything >> other than an ulcer. > It would take far too long to explain the whole show. If you have the time and interest > contact me privately and I'll show you what I'm up to. But I already waste far too much > of the list's time. Au contraire. https://www.mikeash.com/getting_answers.html#provide >> % () { eval "print -rC1 -- ${(q)@}" } a 'b c' d 'e f g' >> a >> b c >> d >> e f g >> >> > Thing is that if those args were going to grep and they were filenames you'd end up with: > > $ grep [some search string] a b c d e f g No. This has nothing to do with grep and everything to do with how you assemble the eval argument. % () { emulate -L zsh && eval "grep ${(q)@}" } pattern 'file 1' file2 'file the third' grep: file 1: No such file or directory grep: file2: No such file or directory grep: file the third: No such file or directory -- vq
On 2021-02-09 5:52 p.m., Greg Klanderman wrote: It works Greg, but it misses the crux of my problem which is that all my wrappers run everything thru 'eval' as below (simplified from your original): grep_wrapper () { grepargs=() sstring= while [[ $# -gt 0 ]] ; do arg="$1" shift grepargs+=( "$arg" ) # This is what Peter was saying. done # grep --color=always -i -- "${grepargs[@]}" # Works fine but ... sstring="grep --color=always -i -- ${grepargs[@]}" # I need to save to a scalar ... print -rS "$sstring" # Write to history ... eval "$sstring" # And 'eval'. print -r "\n$sstring" } 9 /aWorking/Zsh/Source/Wk 4 $ . test; grep_wrapper 'on the current' 'i,2,light edit' 'i,1,old stable' grep: the: No such file or directory grep: current: No such file or directory grep: i,2,light: No such file or directory grep: edit: No such file or directory grep: i,1,old: No such file or directory grep: stable: No such file or directory grep --color=always -i -- on the current i,2,light edit i,1,old stable ... as before the quotes are stripped if I do it my way. But as I discovered last night: 9 /aWorking/Zsh/Source/Wk 4 $ . test; grep_wrapper "'on the current' 'i,2,light edit' 'i,1,old stable'" i,2,light edit:Find any command named 'mtr*' on the current path. i,1,old stable:Find any command named 'mtr*' on the current path. grep --color=always -i -- 'on the current' 'i,2,light edit' 'i,1,old stable' ... by simply double quoting the argument string, the single quotes are preserved and 'eval' works. She is NOT being unreasonable. Just do it her way and everything will be fine. A lesson for life.
> On 10 February 2021 at 15:18 Ray Andrews <rayandrews@eastlink.ca> wrote:
> sstring="grep --color=always -i -- ${grepargs[@]}" # I need to save
> to a scalar ...
> print -rS "$sstring" # Write to
> history ...
> eval "$sstring" # And 'eval'.
Don't turn it into a string here.
fullargs=(grep --color=always -i -- "${grepargs[@]}")
print -rS "$fullargs"
"${fullargs[@]}"
No eval needed, the arguments are now exactly the correct command arguments.
Actually, the
"${stuff[@]}"
is only needed to preserve empty arguments. It's a good habit to get into,
but you can think about it more simply as just
$stuff
i.e. a set of array elements that turn into a set of command arguments.
pws
On 2021-02-10 7:47 a.m., Peter Stephenson wrote:
>
> No eval needed, the arguments are now exactly the correct command arguments.
>
If you look at this one case indeed no eval is needed, but in the
broader context of the way all
my functions work, the eval is unavoidable. Or is it? Maybe not.
> On Feb 10, 2021, at 10:18 AM, Ray Andrews <rayandrews@eastlink.ca> wrote:
>
> She is NOT being unreasonable. Just do it her way and everything will be fine.
> A lesson for life.
I'd appreciate it if you kept these "women, AMIRITE?" remarks to
yourself, thanks.
vq
> On 10 February 2021 at 16:19 Ray Andrews <rayandrews@eastlink.ca> wrote:
> On 2021-02-10 7:47 a.m., Peter Stephenson wrote:
> >
> > No eval needed, the arguments are now exactly the correct command arguments.
> >
> If you look at this one case indeed no eval is needed, but in the
> broader context of the way all
> my functions work, the eval is unavoidable. Or is it? Maybe not.
As always, that depends what you're actually doing, which is a superset of
what you're telling us you're doing, but in general it shouldn't be a problem
--- unless you know you need an extra set of expansions, it's possible to
prepare the arguments you need in the array to be executed as a command
line.
What I mean is, the cases where this *wouldn't* work would be like this...
fullargs=(some stuff '$to_be_expanded_later')
So somewhere down the line you're going to assign to to_be_expanded_later,
and you want that to be expanded when you execute the command line.
*Now* you need the extra eval. But if you're not doing anything like
that, you've just plain old command line arguments and no eval is needed.
pws
On Wed, Feb 10, 2021 at 8:19 AM Ray Andrews <rayandrews@eastlink.ca> wrote:
>
> On 2021-02-10 7:47 a.m., Peter Stephenson wrote:
> >
> > No eval needed, the arguments are now exactly the correct command arguments.
> >
> If you look at this one case indeed no eval is needed, but in the
> broader context of the way all
> my functions work, the eval is unavoidable. Or is it? Maybe not.
Even if you do need "eval", you should still use an array. That is, instead of
sstring="grep --color=always -i -- ${grepargs[@]}"
eval "$sstring"
use
sarray=(grep --color=always -i -- "${grepargs[@]}")
eval "${(@q)sarray}"
Using (@q) causes each individual array element to be quoted
separately. The double quotes then expand it in the manner of
"${sarray[@]}".
On 2021-02-10 8:29 a.m., Peter Stephenson wrote:
>
> As always, that depends what you're actually doing, which is a superset of
> what you're telling us you're doing,
It's hard to put it briefly and still accurately. I went down a certain
path when I first
got involved and it might not have been the right path nevertheless it
has a certain
power. My wrappers evolve and evolve with more and more filtering and
coloring
and columnizing and massaging and end up with some monstrous actual command
strings to execute. By being able to recall the actual command, I can
edit it to find
some bug very quickly. My function assembled: " grep-i -- arg arg "
when what was
wanted was: " grep -i -- arg arg " . Once I know where the bug is, I can
figure out
where it came from in my function very quickly. So I assemble my
string, save it
to history (optionally) and then eval it to fire it off. Up arrow, and
voila! I have the
guts of what my function did, available to tweak. So:
9 /aWorking/Zsh/Source/Wk 3 $ l ,H l,*
LISTING of "l,*": all file types, INsensitive. Sorting upside down by:
Mod. Time:
11434 [2021-01-07--08:49] l,6,NO H
10516 [2021-01-07--08:49] l,4,BUG and case now insensitive
...
15988 [2021-02-03--20:06] l,51,colorize all matches if leading wildcard
15614 [2021-02-04--16:34] l,52,bug with totals
Items found: 52
Total bytes in this directory: 3.5M
Total including subdirs: 16M
... and how did that actually happen? " ,H " orders a write to history
so up arrow and:
9 /aWorking/Zsh/Source/Wk 3 $ ls -AFrGgdt --time-style='+[%F--%H:%M]'
--group-directories-first --color=always (#i)l,* 2> /dev/null | sed
-r "s|^(.{10} {1,2}[[:digit:]]{1,2} )| |" | egrep -v '^total' | egrep
-i --color=always "^|] l,*" | sed -r
"s/\x1b\[01;31m\x1b\[K\] /\] \x1b\[01;31m\x1b\[K/g"
... that's the actual guts of the thing. No, I'm not insane, I just
like color, prefer to remove all the permissions stuff since it it
irrelevant to me, etc. But the whole show requires the whole command to
exist as a string so it can be saved and thus it needs 'eval' to fire it
off ... unless I'm mistaken.
If that makes any sense. It all works quite fine but there are these
quoting issues now and then.
On 2021-02-10 10:08 a.m., Bart Schaefer wrote:
> sarray=(grep --color=always -i -- "${grepargs[@]}")
> eval "${(@q)sarray}"
>
> Using (@q) causes each individual array element to be quoted
> separately. The double quotes then expand it in the manner of
> "${sarray[@]}".
>
That looks like exactly what I've been wanting. Test later.
On Wed, Feb 10, 2021 at 12:54 PM Ray Andrews <rayandrews@eastlink.ca> wrote:
>
> [...] So I assemble my string, save it
> to history (optionally) and then eval it to fire it off.
> [...] But the whole show requires the whole command to
> exist as a string so it can be saved and thus it needs 'eval' to fire it
> off ... unless I'm mistaken.
Following on my last message, it should work fine to do
print -sr -- "${(@q)sarray}"
You probably want (@q-) there, to minimize the number of quoted
things, or (@q+) if you use a lot of unicode characters or control
strings.
Only "print -S" requires a single string argument.
On 2021-02-10 2:14 p.m., Bart Schaefer wrote:
>
> You probably want (@q-) there, to minimize the number of quoted
> things, or (@q+) if you use a lot of unicode characters or control
> strings.
Seems to be working fine:
grep_wrapper ()
{
grepargs=()
sstring=
while [[ $# -gt 0 ]] ; do
arg="$1"
shift
grepargs+=( "$arg" )
done
sarray=(grep --color=always -i -- "${grepargs[@]}")
print -sr -- "${(@q)sarray}"
echo "\nwhat should be going to history:"
print -r -- "${(@q)sarray}"
echo "\neval:"
eval "${(@q)sarray}"
}
Test run:
9 /aWorking/Zsh/Source/Wk 3 $ . test; grep_wrapper 'on the current'
'i,2,light edit' 'i,1,old stable'
what should be going to history:
grep --color=always -i -- on\ the\ current i,2,light\ edit i,1,old\ stable
eval:
i,2,light edit:Find any command named 'mtr*' on the current path.
i,1,old stable:Find any command named 'mtr*' on the current path.
Up arrow to recall and then re-execute:
9 /aWorking/Zsh/Source/Wk 3 $ grep --color=always -i -- on\ the\ current
i,2,light\ edit i,1,old\ stable
i,2,light edit:Find any command named 'mtr*' on the current path.
i,1,old stable:Find any command named 'mtr*' on the current path.
--------------------------------------------------------------------------------------------------------------------
So, the single quotes might be protected by double quoting as I had it
before,
or we can use the magic of ' (q) ' to protect the spaces with
backslashes even
though the single quotes themselves are gone. Or, using ' (q-) ':
9 /aWorking/Zsh/Source/Wk 3 $ . test; grep_wrapper 'on the current'
'i,2,light edit' 'i,1,old stable'
what should be going to history:
grep --color=always -i -- 'on the current' 'i,2,light edit' 'i,1,old stable'
eval:
i,2,light edit:Find any command named 'mtr*' on the current path.
i,1,old stable:Find any command named 'mtr*' on the current path.
Up arrow and re-execute:
9 /aWorking/Zsh/Source/Wk 3 $ grep --color=always -i -- 'on the current'
'i,2,light edit' 'i,1,old stable'
i,2,light edit:Find any command named 'mtr*' on the current path.
i,1,old stable:Find any command named 'mtr*' on the current path.
-------------------------------------------------------------------------------------------------------
Seems cleaner, the single quotes are themselves protected or replaced so
that
it not only runs right, it looks right; on recall it is visually
exactly the same. And is it not
intuitive that, since single quotes already protect their string, if you
add protection for the
single quotes themselves, you then have a perfectly protected string?
It can be
passed from one function to the next unchanged. This is the holy grail
for me.
Any gotchas? Or is that robust? Can I use it everywhere? Seems
right. The array
separates the arguments even if the might look like they run together in
a string.
Thanks all.
> On Feb 11, 2021, at 11:31 AM, Ray Andrews <rayandrews@eastlink.ca> wrote: > > while [[ $# -gt 0 ]] ; do > arg="$1" > shift > grepargs+=( "$arg" ) > done Why are you still accreting grepargs like this? Peter has already shown you how to do this. grepargs=("$@") Unless your actual code actually modifies the args before adding them to the array? > Seems cleaner, the single quotes are themselves protected or replaced so that > it not only runs right, it looks right; on recall it is visually exactly the same. The command saved to history is correct, but it is not always exactly what you entered. % grep_wrapper on\ the\ current i,2,light\ edit i,1,old\ stable what should be going to history: grep --color=always -i -- 'on the current' 'i,2,light edit' 'i,1,old stable' [...] % pat='on the current' foo='i,2,light edit' bar='i,1,old stable' % grep_wrapper $pat $foo $bar what should be going to history: grep --color=always -i -- 'on the current' 'i,2,light edit' 'i,1,old stable' [...] A ${(q-)foo} expansion basically re-quotes the value of foo so it works correctly with eval, in whatever way is requested. It doesn't know the value's origins. > And is it not intuitive that, since single quotes already > protect their string, if you add protection for the single > quotes themselves, you then have a perfectly protected string? That's not how quoting works. Quote levels don't nest to produce some kind of super-quoting. You're misinterpreting the results of your experimentation. % foo='a b c' % print -r 'protected: $foo' protected: $foo % print -r "'not protected: $foo'" 'not protected: a b c' The double quotes do protect the single quotes from being interpreted by the shell, but they also permit expansions. vq
On 2021-02-11 9:19 a.m., Lawrence Velázquez wrote: > > Why are you still accreting grepargs like this? Peter has already > shown you how to do this. > > grepargs=("$@") > > Unless your actual code actually modifies the args before adding > them to the array? Not modified but the ordering is different so I hafta grab them one at a time. Good point tho, all else equal I see that Peter's method is better. > [...] > > % pat='on the current' foo='i,2,light edit' bar='i,1,old stable' > % grep_wrapper $pat $foo $bar > > what should be going to history: > grep --color=always -i -- 'on the current' 'i,2,light edit' 'i,1,old stable' Yes, point made. I know that if I want something expanded I don't single quote it. But indeed I shouldn't say 'exactly as typed' because I know that variables will be expanded. > [...] > > A ${(q-)foo} expansion basically re-quotes the value of foo so > it works correctly with eval, in whatever way is requested. It > doesn't know the value's origins. That's a subtle but important point. I could think of it as 'protection' but then how did the variable expand? As you say it must be seen as re-quoting. Thanks for catching it. > That's not how quoting works. Quote levels don't nest to produce > some kind of super-quoting. You're misinterpreting the results of > your experimentation. > > % foo='a b c' > % print -r 'protected: $foo' > protected: $foo > % print -r "'not protected: $foo'" > 'not protected: a b c' > > The double quotes do protect the single quotes from being interpreted > by the shell, but they also permit expansions. That is a critical point. You know, it is so easy to think you've 'got it' when you don't. I'm going to take the above and write it to a sticky note a stick it to my desk. Very insightful of you to realize that I'm not seeing things right, many thanks.