Zsh Mailing List Archive
Messages sorted by:
Re: Difference between ~ except and ^ not in fng
- X-seq: zsh-users 18781
- From: Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
- To: zsh-users@xxxxxxx
- Subject: Re: Difference between ~ except and ^ not in fng
- Date: Sat, 03 May 2014 13:37:50 +0100
- In-reply-to: Message from zzapper <email@example.com> of "Sat, 03 May 2014 10:38:37 -0000." <XnsA322766E2602Bdavidrayninfocouk@18.104.22.168>
- List-help: <mailto:firstname.lastname@example.org>
- List-id: Zsh Users List <zsh-users.zsh.org>
- List-post: <mailto:email@example.com>
- Mailing-list: contact zsh-users-help@xxxxxxx; run by ezmlm
> I'm trying to formulate some examples that show the difference between ~ &
> But everytime I do I find I can do it with either.
This is now in the FAQ.
3.27: What are these `^' and `~' pattern characters, anyway?
The characters `^' and `~' are active when the option
EXTENDED_GLOB is set. Both are used to exclude patterns, i.e. to
say `match something other than ...'. There are some confusing
differences, however. Here are the descriptions for `^' and `~'.
`^' means `anything except the pattern that follows'. You can
think of the combination ^pat as being like a * except
that it doesn't match pat. So, for example, `myfile^.txt'
matches anything that begins with myfile except myfile.txt.
Because it works with patterns, not just strings, `myfile^*.c'
matches anything that begins with myfile unless it ends with
.c, whatever comes in the middle --- so it matches myfile1.h
but not myfile1.c.
Also like `*', `^' doesn't match across directories if you're
matching files when `globbing', i.e. when you use an unquoted pattern
in an ordinary command line to generate file names. So
`^dir1/^file1' matches any subdirectory of the current directory
except one called dir1, and within any directory it matches it
picks any file except one called file1. So the overall pattern
matches dir2/file2 but not dir1/file1 nor dir1/file2 nor
dir2/file1. (The rule that all the different bits of the pattern
must match is exactly the same as for any other pattern character,
it's just a little confusing that what does match in each bit is
found by telling the shell not to match something or other.)
As with any other pattern, a `^' expression doesn't treat the
character `/' specially if it's not matching files, for example
when pattern matching in a command like `[[ $string = ^pat1/pat2 ]]'.
Here the whole string pat1/pat2 is treated as the argument that
follows the `^'. So anything matches but that one string
It's not obvious what something like `[[ $string = ^pat1^pat2 ]]'
means. You won't often have cause to use it, but the rule is that
each `^' takes everything that follows as an argument (unless
it's already inside parentheses --- I'll explain this below). To see
this more clearly, put those arguments in parentheses: the pattern is
equivalent to `^(pat1^(pat2))'. where now you can see exactly what
each `^' takes as its argument. I'll leave it as an exercise for
you to work out what this does and doesn't match.
`~' is always used between two patterns --- never right at the
beginning or right at the end. Note that the other special meaning of
`~', at the start of a filename to refer to your home directory or
to another named directory, doesn't require the option
EXTENDED_GLOB to be set. (At the end of an argument `~' is
never special at all. This is useful if you have Emacs backup files.)
It means `match what's in front of the tilde, but only if it doesn't
match what's after the tilde'. So `*.c~f*' matches any file
ending in .c except one that begins with f. You'll see that,
unlike `^', the parts before and after the `~' both refer
separately to the entire test string.
For matching files by globbing, `~' is the only globbing operator
to have a lower precedence than `/'. In other words, when you
have `/a/path/to/match~/a/path/not/to/match' the `~' considers
what's before as a complete path to a file name, and what's after as a
pattern to match against that file. You can put any other pattern
characters in the expressions before and after the `~', but as I
said the pattern after the ~ is really just a single pattern to
match against the name of every file found rather than a pattern to
generate a file. That means, for example, that a * after the
~ will match a /. If that's confusing, you can think of
how `~' works like this: take the pattern on the left, use it as
normal to make a list of files, then for each file found see if it
matches the pattern on the right and if it does take that file out of
the list. Note, however, that this removal of files happens
immediately, before anything else happens to the file list --- before
any glob qualifiers are applied, for example.
One rule that is common to both `^' and `~' is that they can
be put inside parentheses and the arguments to them don't extend past
the parentheses. So `(^README).txt' matches any file ending in
.txt unless the string before that was README, the same as
`*.txt~README.txt' or `(*~README).txt'. In fact, you can
always turn `^something' into `(*~something)', where
`something' mustn't contain / if the pattern is being used for
Likewise, `abc(<->~<10-100>).txt' matches a file consisting of
abc, then some digits, then .txt, unless the digits happen to
match a number from 10 to 100 inclusive (remember the handy `<->'
pattern for matching integers with optional limits to the range). So
this pattern matches abc1.txt or abc200.txt but not
abc20.txt nor abc100.txt nor even abc0030.txt. However,
if you're matching files by globbing note you can't put `/'s
inside the parentheses since the groups can't stretch across multiple
directories. (You can do that, of course, whenever the character
`/' isn't special.) This means that you need to take care when
using exclusions across multiple directories; see some examples below.
You may like to know that from zsh 5.0.3 you can disable any pattern
character separately. So if you find `^' gets in your way and
you're happy using `~', put `disable -p "^"' in ~/.zshrc.
You still need to turn on EXTENDED_GLOB; the disable command
only deactivates things that would otherwise be active, you can't
specially enable something not allowed by the syntax options in effect.
Here are some examples with files to illustrate the points. We'll
assume the option EXTENDED_GLOB is set and none of the pattern
characters is disabled.
1) `**/foo~*bar*' matches any file called `foo' in any
subdirectory, except where `bar' occurred somewhere in the path.
For example, `users/barstaff/foo' will be excluded by the `~'
operator. As the `**' operator cannot be grouped (inside
parentheses it is treated as `*'), this is one way to exclude some
subdirectories from matching a `**'. Note that this can be quite
inefficent because the shell performs a complete search for
`**/foo' before it uses the pattern after the `~' to exclude
files from the match. The file is excluded if `bar' occurs
anywhere, in any directory segment or the final file name.
2) The form `(^foo/)#' can be used to match any hierarchy of
directories where none of the path components is foo. For
example, `(^CVS/)#' selects all subdirectories to any depth
except where one component is named `CVS'. (The form
`(pat/)#' is very useful in other cases; for example,
`(../)#.cvsignore' finds the file .cvsignore if it exists
in the current directory or any parent.)
Messages sorted by: