zsh-users
 help / color / mirror / code / Atom feed
* ${name/pattern/repl} with negated pattern
@ 2014-05-15 12:26 Roman Neuhauser
  2014-05-15 15:32 ` Peter Stephenson
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Roman Neuhauser @ 2014-05-15 12:26 UTC (permalink / raw)
  To: zsh-users

this snippet in zshcontrib(1):

    for i in "$@"; do
        if [[ $i == sh/* ]]; then
            [[ -n $s ]] && s=$s,
            s=${s}$i
        fi
    done

can be reduced to

    s=${(j:,:)${@/#(^sh)\/*}}

i *thought*, based on the man page, that this would work as well:

    s=${(j:,:)${@/#^sh\/*}}

alas:

    > a=(x/foo y/bar x/baz y/qux)
    > echo ${a/#(^x)\/*} # ok
    x/foo x/baz
    > echo ${a/#^x\/*} # wtf
    /foo /baz

what's happening here?

zshexpn(1) suggests parentheses are not required:

    ^x     (Requires EXTENDED_GLOB to be set.) Matches anything except
           the pattern x. This has a higher precedence than `/', so
           `^foo/bar' will search directories in `.' except `./foo' for
           a file named `bar'.

though this description of a ksh glob operator is suspicious:

    !(...) Match anything but the expression in parentheses.
           (Like `(^(...))'.)


while i'm here: the whole workaround is cumbersome, you need to negate
the pattern (might be difficult in some cases) and actually get as many
items back as are in the input array (thus the nested parameter
expansion, ${(j:,:)a/#(^x)\/*} produces "x/foo,,x/baz,").
i hoped zsh would have a direct way to expand only those items of an
array that match a pattern, eg. ${@:/#sh\/*}.

doable?

-- 
roman


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

* Re: ${name/pattern/repl} with negated pattern
  2014-05-15 12:26 ${name/pattern/repl} with negated pattern Roman Neuhauser
@ 2014-05-15 15:32 ` Peter Stephenson
  2014-05-15 18:18   ` Roman Neuhauser
  2014-05-15 15:45 ` Bart Schaefer
  2014-05-15 18:39 ` Roman Neuhauser
  2 siblings, 1 reply; 8+ messages in thread
From: Peter Stephenson @ 2014-05-15 15:32 UTC (permalink / raw)
  To: zsh-users

On Thu, 15 May 2014 14:26:41 +0200
Roman Neuhauser <neuhauser@sigpipe.cz> wrote:
>     > a=(x/foo y/bar x/baz y/qux)
>     > echo ${a/#^x\/*} # wtf
>     /foo /baz
> 
> what's happening here?

It took me a while to work it out.

/# means "find this pattern at the head of the string and replace it".
By the usual rule it's the longest pattern that matches.  Note it's not
anchored to the end of the string --- I think you need ${a/#%^x\/*} if
you stick with "/", however see the discussion below.

^ means "anything, and I do mean anything, that doesn't match the
following pattern".  You can see that the initial "x" doesn't match the
pattern, because the pattern requires the "/" to match.  However, x/ or
anything longer does match the pattern because of the *.  So the longest
string at the head that doesn't match the pattern is "x".  So it removes
that.

This has no effect on the y/... bits; the whole string of those fails to
match x/*, so they're removed completely.

I think you'd be better off with ${a:#^x/*}, which removes anything that
matches the pattern after from beginning to end, with no funny business
about anchoring.  It still has the oddity that you need to negate the
pattern after, but as far as I can see this always works logically.

I think the way to do it without negating the pattern is to use the (M)
flag, saying substitute the matched part, rather than the rest --- the
matched part here would usually be everything you're throwing away with
this operator.

So try ${(M)a:#x/*}.

pws


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

* Re: ${name/pattern/repl} with negated pattern
  2014-05-15 12:26 ${name/pattern/repl} with negated pattern Roman Neuhauser
  2014-05-15 15:32 ` Peter Stephenson
@ 2014-05-15 15:45 ` Bart Schaefer
  2014-05-15 16:37   ` Roman Neuhauser
  2014-05-15 18:39 ` Roman Neuhauser
  2 siblings, 1 reply; 8+ messages in thread
From: Bart Schaefer @ 2014-05-15 15:45 UTC (permalink / raw)
  To: zsh-users

On May 15,  2:26pm, Roman Neuhauser wrote:
}
}     > a=(x/foo y/bar x/baz y/qux)
}     > echo ${a/#(^x)\/*} # ok
}     x/foo x/baz
}     > echo ${a/#^x\/*} # wtf
}     /foo /baz
} 
} what's happening here?

You've forgotten that parameter expansion is a pattern match rather than
a filename glob and that slashes have no special significance in pattern
matching, so ^x\/* is ^(x\/*) despite the "higher precedence" mention.
 
} while i'm here: the whole workaround is cumbersome, you need to negate
} the pattern (might be difficult in some cases) and actually get as many
} items back as are in the input array (thus the nested parameter
} expansion, ${(j:,:)a/#(^x)\/*} produces "x/foo,,x/baz,").

You can work out why this happens if you read through the "rules for
substitution" in the Parameter Expansion section.  The short answer is
that you have to use a nested expansion to cause the array to be fully
normalized before you join its elements:

    ${(j:,:)${a/#(^x)\/*}}

If you're going to put that inside double quotes, additional gyrations
are required.

} i hoped zsh would have a direct way to expand only those items of an
} array that match a pattern, eg. ${@:/#sh\/*}.

Not sure what #sh\/* means (you can't start an extendedglob pattern with
the "#" repetition operator, though for some reason parameter expansion
doesn't gripe about it) but the (M) qualifier with the :# susbstition
should do what you want:

    ${(M)@:#pattern}


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

* Re: ${name/pattern/repl} with negated pattern
  2014-05-15 15:45 ` Bart Schaefer
@ 2014-05-15 16:37   ` Roman Neuhauser
  2014-05-15 17:16     ` Bart Schaefer
  0 siblings, 1 reply; 8+ messages in thread
From: Roman Neuhauser @ 2014-05-15 16:37 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-users

# schaefer@brasslantern.com / 2014-05-15 08:45:50 -0700:
> On May 15,  2:26pm, Roman Neuhauser wrote:
> }
> }     > a=(x/foo y/bar x/baz y/qux)
> }     > echo ${a/#(^x)\/*} # ok
> }     x/foo x/baz
> }     > echo ${a/#^x\/*} # wtf
> }     /foo /baz
> } 
> } what's happening here?
> 
> You've forgotten that parameter expansion is a pattern match rather than
> a filename glob and that slashes have no special significance in pattern
> matching, so ^x\/* is ^(x\/*) despite the "higher precedence" mention.

no, i know the manpage says / is only special in filename generation.
sorry for being so implicit with the question, i should know better.

why does it remove the initial "x" from "x/foo" and "x/baz"?

> } i hoped zsh would have a direct way to expand only those items of an
> } array that match a pattern, eg. ${@:/#sh\/*}.
> 
> Not sure what #sh\/* means (you can't start an extendedglob pattern with
> the "#" repetition operator, though for some reason parameter expansion
> doesn't gripe about it)

zshexpn(1):

    ${name/pattern/repl}
    ${name//pattern/repl}
      [...]
      The pattern may begin with a `#', in which case the pattern must
      match at the start of the string, or `%', in which case it must
      match at the end of the string, or `#%' in which case the pattern
      must match the entire string.

> but the (M) qualifier with the :# susbstition
> should do what you want:
> 
>     ${(M)@:#pattern}

exactly what i'm after, thank you!

-- 
roman


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

* Re: ${name/pattern/repl} with negated pattern
  2014-05-15 16:37   ` Roman Neuhauser
@ 2014-05-15 17:16     ` Bart Schaefer
  0 siblings, 0 replies; 8+ messages in thread
From: Bart Schaefer @ 2014-05-15 17:16 UTC (permalink / raw)
  To: zsh-users

On May 15,  6:37pm, Roman Neuhauser wrote:
}
}     ${name/pattern/repl}
}     ${name//pattern/repl}
}       [...]
}       The pattern may begin with a `#', in which case the pattern must
}       match at the start of the string, or `%', in which case it must
}       match at the end of the string, or `#%' in which case the pattern
}       must match the entire string.

Indeed, I'd forgotten about that.  Still:

torch% setopt extendedglob
torch% torch% echo #foo
zsh: bad pattern: #foo
torch% print ${a:##foo}

torch% 

Evidently the syntax allowing the pattern to begin with a "#" is applied
in the name:# case as well as in the name/ case, even though it should
be meaningless for :#.


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

* Re: ${name/pattern/repl} with negated pattern
  2014-05-15 15:32 ` Peter Stephenson
@ 2014-05-15 18:18   ` Roman Neuhauser
  0 siblings, 0 replies; 8+ messages in thread
From: Roman Neuhauser @ 2014-05-15 18:18 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: zsh-users

# p.stephenson@samsung.com / 2014-05-15 16:32:19 +0100:
> ^ means "anything, and I do mean anything, that doesn't match the
> following pattern".  You can see that the initial "x" doesn't match the
> pattern, because the pattern requires the "/" to match.  However, x/ or
> anything longer does match the pattern because of the *.  So the longest
> string at the head that doesn't match the pattern is "x".  So it removes
> that.

thanks, i suspected strongly it was my confusion with the negation.
i'll have to go and get some practice with pattern matching, i used to
do better.  and your suggestion of #% is spot on, it does fix it.
 
> I think you'd be better off with ${a:#^x/*},

agreed.

> So try ${(M)a:#x/*}.

yep, this is IMO easiest.

-- 
roman


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

* Re: ${name/pattern/repl} with negated pattern
  2014-05-15 12:26 ${name/pattern/repl} with negated pattern Roman Neuhauser
  2014-05-15 15:32 ` Peter Stephenson
  2014-05-15 15:45 ` Bart Schaefer
@ 2014-05-15 18:39 ` Roman Neuhauser
  2014-05-16  8:40   ` Peter Stephenson
  2 siblings, 1 reply; 8+ messages in thread
From: Roman Neuhauser @ 2014-05-15 18:39 UTC (permalink / raw)
  To: zsh-users

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

# neuhauser@sigpipe.cz / 2014-05-15 14:26:41 +0200:
> this snippet in zshcontrib(1):
> 
>     for i in "$@"; do
>         if [[ $i == sh/* ]]; then
>             [[ -n $s ]] && s=$s,
>             s=${s}$i
>         fi
>     done
> 
> can be reduced to
[...]

please find attached a tiny patch for Doc/Zsh/contrib.yo

-- 
roman

[-- Attachment #2: 0001-vcs_info-simplify-vi-hgbookmarks-LPAR-RPAR-in-Doc-Zs.patch --]
[-- Type: text/x-diff, Size: 1475 bytes --]

>From 0e40d433f1087367b718d3426eb22aa2fdd921e9 Mon Sep 17 00:00:00 2001
From: Roman Neuhauser <rneuhauser@suse.cz>
Date: Thu, 15 May 2014 20:37:01 +0200
Subject: [PATCH] vcs_info: simplify +vi-hgbookmarks+LPAR()RPAR() in
 Doc/Zsh/contrib.yo

---
 Doc/Zsh/contrib.yo | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 9f59f23..efdb436 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -1504,22 +1504,15 @@ function +vi-hgbookmarks+LPAR()RPAR() {
     # This makes the bookmarks string use only those
     # bookmarks. If there's more than one, it
     # concatenates them using commas.
-    local s i
     # The bookmarks returned by `hg' are available in
     # the functions positional parameters.
-    (( $# == 0 )) && return 0
-    for i in "$@"; do
-        if [[ $i == sh/* ]]; then
-            [[ -n $s ]] && s=$s,
-            s=${s}$i
-        fi
-    done
+    local s="${(Mj:,:)@:#sh/*}"
     # Now, the communication with the code that calls
     # the hook functions is done via the hook_com[]
     # hash. The key, at which the `gen-hg-bookmark-string'
     # hook looks at is `hg-bookmark-string'. So:
     hook_com[hg-bookmark-string]=$s
-    # And to signal, that we want to use the sting we
+    # And to signal, that we want to use the string we
     # just generated, set the special variable `ret' to
     # something other than the default zero:
     ret=1
-- 
1.9.2


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

* Re: ${name/pattern/repl} with negated pattern
  2014-05-15 18:39 ` Roman Neuhauser
@ 2014-05-16  8:40   ` Peter Stephenson
  0 siblings, 0 replies; 8+ messages in thread
From: Peter Stephenson @ 2014-05-16  8:40 UTC (permalink / raw)
  To: zsh-users

On Thu, 15 May 2014 20:39:57 +0200
Roman Neuhauser <neuhauser@sigpipe.cz> wrote:
> please find attached a tiny patch for Doc/Zsh/contrib.yo

I took the opportunity to make further minor changes to the comments
already there at the same time.

pws


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

end of thread, other threads:[~2014-05-16  8:51 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-05-15 12:26 ${name/pattern/repl} with negated pattern Roman Neuhauser
2014-05-15 15:32 ` Peter Stephenson
2014-05-15 18:18   ` Roman Neuhauser
2014-05-15 15:45 ` Bart Schaefer
2014-05-15 16:37   ` Roman Neuhauser
2014-05-15 17:16     ` Bart Schaefer
2014-05-15 18:39 ` Roman Neuhauser
2014-05-16  8:40   ` Peter Stephenson

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