zsh-users
 help / color / mirror / code / Atom feed
* Mix and match parameter expansion flags and sub-scripting flags + quoting
@ 2017-12-29 15:29 Jim
  2017-12-29 18:43 ` Bart Schaefer
  0 siblings, 1 reply; 4+ messages in thread
From: Jim @ 2017-12-29 15:29 UTC (permalink / raw)
  To: zsh


[-- Attachment #1.1: Type: text/plain, Size: 2570 bytes --]

Hello,

The intent is, using only "native zsh" tools, return the 'shell'
as set in /etc/passwd for my own $USER without using any external
utilities, gnu or otherwise. As in UNIX/Linux, zsh also has more then
one way of doing the same thing. So as a training exercise I decided
to use as many variants as I could to do the same thing. In the process
I found that what I though would work, did not return the intended
result. A number of attempts to fix, ended up producing, if nothing
else, syntax errors.

Some of my early trial and error, was not so successful, so I ended up
testing quite a few variations of quoting, patterns, etc... in an
attempt to determine what was going on. From the examples below you
will see a couple of different patterns and subscript numbers and
quoting around '${(f)$(<TestFile)}'. As a note all three variations of
quoting of the fore mentioned code appear to return the same result.

TestFile=${${TestFile:-$1}:-/etc/passwd} # set default file

The following works with or w/o the "external" quotes:
shell="${${${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[(ws/:/)7]}"
as does:
shell="${${(s/:/)"${(f)"$(<${TestFile})"}"[(r)${USER}:*]}[-1]}"

The following returns the correct result, only if there are no
"external" quotes(as shown):
shell=${${(s/:/)${(M)"${(f)$(<${TestFile})}":#${USER}:*}}[-1]}

Would like to understand what is going on here when "external"
quotes a present.

The following does not produce any errors, but it also doesn't return
the intended output, with or w/o "external" quotes:
shell="${${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}[(ws/:/)-1]}"

I thought this was the proper use of zsh tools, but since it isn't
returning the intended results, it must be incorrect.  Any thoughts
on what I am doing wrong and or assumptions made?

Also, depending on patterns, subscripts, and in one known case, quoting;
the results returned are all over the place. It could be a "blank line",
or the last field of the last line of the passwd file, or the last field
of the first line + the first field of the second line, or the entire
line for $USER.

Note, if your USER entry is the last line of the passwd file, you can
get the correct results, even though the code is wrong. Found this the
hard way.

Any pointers as to how to proceed to understand what is going on would
be appreciated. Thanks in advance.

My distributions currently distributes ZSH_VERSION 5.3.1.

Sincerely,

Jim

P.S. The attached script shows the variations I've tried.
   I don't expect anyone to fix any incorrect scripting,
   that's on me.

[-- Attachment #1.2: Type: text/html, Size: 6650 bytes --]

[-- Attachment #2: testscript --]
[-- Type: application/octet-stream, Size: 3726 bytes --]


#TestFile=${${TestFile:-$1}:-${HOME}/test/files/pw}
TestFile=${${TestFile:-$1}:-/etc/passwd}

  print "Group 1 with outer quotes"
  shell="${${${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[(ws/:/)7]}" ; print ${shell}
  shell="${${${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[(ws/:/)-1]}" ; print ${shell}
  print "Group 1 w/o outer quotes"
  shell=${${${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[(ws/:/)7]} ; print ${shell}
  shell=${${${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[(ws/:/)-1]} ; print ${shell}
  print "Group 2 with outer quotes"
  shell="${${${(f)"$(<${TestFile})"}[(r)${USER}:*]}[(ws/:/)7]}" ; print ${shell}
  shell="${${${(f)"$(<${TestFile})"}[(r)${USER}:*]}[(ws/:/)-1]}" ; print ${shell}
  print "Group 2 w/o outer quotes"
  shell=${${${(f)"$(<${TestFile})"}[(r)${USER}:*]}[(ws/:/)7]} ; print ${shell}
  shell=${${${(f)"$(<${TestFile})"}[(r)${USER}:*]}[(ws/:/)-1]} ; print ${shell}
# ------------------------------------------------------------------------------
  print "Group 3 with outer quotes"
  shell="${${(s/:/)${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}}[7]}" ; print ${shell}
  shell="${${(s/:/)${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}}[-1]}" ; print ${shell}
  print "Group 3 w/o outer quotes"
  shell=${${(s/:/)${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}}[7]} ; print ${shell}
  shell=${${(s/:/)${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}}[-1]} ; print ${shell}
  print "Group 4 with outer quotes"
  shell="${${(s/:/)${(M)${(f)"$(<${TestFile})"}:#${USER}:*}}[7]}" ; print ${shell}
  shell="${${(s/:/)${(M)${(f)"$(<${TestFile})"}:#${USER}:*}}[-1]}" ; print ${shell}
  print "Group 4 w/o outer quotes"
  shell=${${(s/:/)${(M)${(f)"$(<${TestFile})"}:#${USER}:*}}[7]} ; print ${shell}
  shell=${${(s/:/)${(M)${(f)"$(<${TestFile})"}:#${USER}:*}}[-1]} ; print ${shell}
# ------------------------------------------------------------------------------
  print "Group 5 with outer quotes"
  shell="${${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}[(ws/:/)7]}" ; print ${shell}
  shell="${${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}[(ws/:/)-1]}" ; print ${shell}
  print "Group 5 w/o outer quotes"
  shell=${${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}[(ws/:/)7]} ; print ${shell}
  shell=${${(M)${(f)"$(<${TestFile})"}:#*:${UID}:${GID}:*}[(ws/:/)-1]} ; print ${shell}
  print "Group 6 with outer quotes"
  shell="${${(M)${(f)"$(<${TestFile})"}:#${USER}:*}[(ws/:/)7]}" ; print ${shell}
  shell="${${(M)${(f)"$(<${TestFile})"}:#${USER}:*}[(ws/:/)-1]}" ; print ${shell}
  print "Group 6 w/o outer quotes"
  shell=${${(M)${(f)"$(<${TestFile})"}:#${USER}:*}[(ws/:/)7]} ; print ${shell}
  shell=${${(M)${(f)"$(<${TestFile})"}:#${USER}:*}[(ws/:/)-1]} ; print ${shell}
# ------------------------------------------------------------------------------
  print "Group 7 with outer quotes"
  shell="${${(s/:/)${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[7]}" ; print ${shell}
  shell="${${(s/:/)${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[-1]}" ; print ${shell}
  print "Group 7 w/o outer quotes"
  shell=${${(s/:/)${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[7]} ; print ${shell}
  shell=${${(s/:/)${(f)"$(<${TestFile})"}[(r)*:${UID}:${GID}:*]}[-1]} ; print ${shell}
  print "Group 8 with outer quotes"
  shell="${${(s/:/)${(f)"$(<${TestFile})"}[(r)${USER}:*]}[7]}" ; print ${shell}
  shell="${${(s/:/)${(f)"$(<${TestFile})"}[(r)${USER}:*]}[-1]}" ; print ${shell}
  print "Group 8 w/o outer quotes"
  shell=${${(s/:/)${(f)"$(<${TestFile})"}[(r)${USER}:*]}[7]} ; print ${shell}
  shell=${${(s/:/)${(f)"$(<${TestFile})"}[(r)${USER}:*]}[-1]} ; print ${shell}
# ------------------------------------------------------------------------------

  print ""
  unset shell


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

* Re: Mix and match parameter expansion flags and sub-scripting flags + quoting
  2017-12-29 15:29 Mix and match parameter expansion flags and sub-scripting flags + quoting Jim
@ 2017-12-29 18:43 ` Bart Schaefer
  2017-12-31 19:01   ` Jim
  0 siblings, 1 reply; 4+ messages in thread
From: Bart Schaefer @ 2017-12-29 18:43 UTC (permalink / raw)
  To: zsh

On Fri, Dec 29, 2017 at 7:29 AM, Jim <linux.tech.guy@gmail.com> wrote:
> Hello,
>
> The intent is, using only "native zsh" tools, return the 'shell'
> as set in /etc/passwd for my own $USER without using any external
> utilities, gnu or otherwise. As in UNIX/Linux, zsh also has more then
> one way of doing the same thing. So as a training exercise I decided
> to use as many variants as I could to do the same thing. In the process
> I found that what I though would work, did not return the intended
> result.

I haven't gone through your cases in any detail, but there are almost
certainly three things causing your problems:
(1) Nested expansions plus quoting cause an array in the inner
expansion to be joined into a string in most cases, unless you use
special syntax to preserve the array
(2) Single-element arrays behave like scalars (strings) in several cases
(3) Subscript syntax applies to scalars, to extract substrings

Those last two put together can cause a lot of head-scratching.

Recent zsh extended ${(A)array} to mean "assure $array continues to be
treated as an array, even when it would otherwise become a string"
specifically to avoid this issue.  Some of your examples work around
it by using the (w) subscript flag, but that isn't quite the same,
because it still starts from a string and re-splits the string before
indexing.  In older zsh for most cases you can preserve array-ness by
using the (@) flag, there are just a few [item (2) above] where that
didn't work which caused us to provide (A).

Compare:

% x=("abc def")
% print ${x[1]}
abc def
% print ${${x}[1]}
abc def
% print "${${x}[1]}"
a
% print ${"${x}"[1]}
a
% print ${"${x}"[(w)1]}
abc
% print ${"${(A)x}"[1]}
abc def
% print "${${(@)x}[1]}"
abc def

Figuring out at what level of a nested expansion you need to add (A)
or (@) is the other part of the equation.  Sometimes you need to add
an extra level of nesting just to force (A) to be applied to the
correct value.

> My distributions currently distributes ZSH_VERSION 5.3.1.

That should be new enough for (A) to work.


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

* Re: Mix and match parameter expansion flags and sub-scripting flags + quoting
  2017-12-29 18:43 ` Bart Schaefer
@ 2017-12-31 19:01   ` Jim
  2018-01-01  8:27     ` dana
  0 siblings, 1 reply; 4+ messages in thread
From: Jim @ 2017-12-31 19:01 UTC (permalink / raw)
  To: zsh

[-- Attachment #1: Type: text/plain, Size: 2850 bytes --]

On Fri, Dec 29, 2017 at 12:43 PM, Bart Schaefer <schaefer@brasslantern.com>
wrote:

> On Fri, Dec 29, 2017 at 7:29 AM, Jim <linux.tech.guy@gmail.com> wrote:
> > Hello,
> >
> > The intent is, using only "native zsh" tools, return the 'shell'
> > as set in /etc/passwd for my own $USER without using any external
> > utilities, gnu or otherwise. As in UNIX/Linux, zsh also has more then
> > one way of doing the same thing. So as a training exercise I decided
> > to use as many variants as I could to do the same thing. In the process
> > I found that what I though would work, did not return the intended
> > result.
>
> I haven't gone through your cases in any detail, but there are almost
> certainly three things causing your problems:
> (1) Nested expansions plus quoting cause an array in the inner
> expansion to be joined into a string in most cases, unless you use
> special syntax to preserve the array
>
(2) Single-element arrays behave like scalars (strings) in several cases
>
(3) Subscript syntax applies to scalars, to extract substrings
>
I can see this from my testing.

>
> Those last two put together can cause a lot of head-scratching.
>
> I believe I do my fair share, and not only on this.

>
> Figuring out at what level of a nested expansion you need to add (A)
> or (@) is the other part of the equation.  Sometimes you need to add
> an extra level of nesting just to force (A) to be applied to the
> correct value.
>
> AFAICT ..."${(f)"$(<TestFile)"}"... is working but it is one of those
head-scratching moments. Inner quotes, outer quotes or both inner and
outer quotes? Testing of all three, all work. I don't believe the problem
is there. The issue appears to be around the matching. The following two
statements appear to return the same line/string/scalar.

shell=${(M)"${(f)"$(<${TestFile})"}":#*:${UID}:${GID}:*}
shell=${"${(f)"$(<${TestFile})"}"[(r)*:${UID}:${GID}:*]}

Applying the subscript to the scalar returns the output I expected in both
cases.

print -l \"${shell}\" ${(t)shell} ${shell[(ws/:/)7]}

When adding the same subscript to both statements, using "nesting",
the resulting output is different for each. The first appears to return an
empty scalar, whereas the second returns the expected output.

shell=${${(M)"${(f)"$(<${TestFile})"}":#*:${UID}:${GID}:*}[(ws/:/)7]} ;
print -l \"${shell}\"

shell=${${"${(f)"$(<${TestFile})"}"[(r)*:${UID}:${GID}:*]}[(ws/:/)7]} ;
print -l \"${shell}\"

The question, for me, is why two different results, by the same subscript,
to
apparently the same output from each type of match? It seems logical
that both should work. Sorry if I'm being like a bulldog that won't let
go.  I do
have other ways that work, as shown above. I would just like to understand
why this doesn't work, even if it's do to my own, current, inability to
grasp the
logic of it.

Thanks,

Jim

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

* Re: Mix and match parameter expansion flags and sub-scripting flags + quoting
  2017-12-31 19:01   ` Jim
@ 2018-01-01  8:27     ` dana
  0 siblings, 0 replies; 4+ messages in thread
From: dana @ 2018-01-01  8:27 UTC (permalink / raw)
  To: linuxtechguy; +Cc: zsh

On 31 Dec 2017, at 13:01, Jim <linux.tech.guy@gmail.com> wrote:
>The question, for me, is why two different results, by the same subscript,
>to
>apparently the same output from each type of match?

In both cases, ${(f)...} is giving you an array. But...

>shell=${${(M)"${(f)"$(<${TestFile})"}":#*:${UID}:${GID}:*}[(ws/:/)7]} ;

In this case, ${(M)...:#...} is giving you another array consisting of the
members of the first one that match the pattern. Your subscript [(ws/:/)7]
doesn't work, then, because as the documentation says...

>w
>If the parameter subscripted is a scalar then this flag makes subscripting work
>on words instead of characters.

The parameter is *not* a scalar (yet), so it has no effect; it's just ignored.
The subscript then is functionally equivalent to [7], and since there is no
index 7 in the array of matches that gives an empty string.

>shell=${${"${(f)"$(<${TestFile})"}"[(r)*:${UID}:${GID}:*]}[(ws/:/)7]} ;

In this case, ${...[(r)...]} is returning the first (and only the first) element
that matches the pattern. Since that element is a scalar, the (w) subscript flag
works the way you want.

dana


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

end of thread, other threads:[~2018-01-01  8:27 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-29 15:29 Mix and match parameter expansion flags and sub-scripting flags + quoting Jim
2017-12-29 18:43 ` Bart Schaefer
2017-12-31 19:01   ` Jim
2018-01-01  8:27     ` dana

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