Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
264 views
in Technique[技术] by (71.8m points)

zsh - Correct usage of ~ parameter expansion flag?

According to man zshexpn (5.0.2):

~ Force string arguments to any of the flags below that follow within the parentheses to be treated as patterns.

For example, using the s flag to perform field splitting requires a string argument:

% print -l ${(s:9:):-"foo893bar923baz"}
foo8
3bar
23baz

My reading of the ~ flag suggests that I should be able to specify a pattern in place of a literal string to split on, so that the following

% print -l ${(~s:<->:):-"foo893bar923baz"}

should produce

foo
bar
baz

Instead, it behaves the same as if I omit the ~, performing no splitting at all.

% print -l ${(s:<->:):-"foo893bar923baz"}
foo893bar923baz
% print -l ${(~s:<->:):-"foo893bar923baz"}
foo893bar923baz
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok, rereading the question, it's the difference between this:

$ val="foo???bar???baz" 
$ print -l ${(s.?.)val}
foo
bar
baz

And this:

$ val="foo???bar???baz" 
$ print -l ${(~s.?.)val}
foo???bar???baz

It operates on the variable, i.e. the "argument" to the split (from your documentation quote). In the first example, we substitute literal ?, and in the second, we treat the variable as a glob, and there are no literal ?, so nothing gets substituted.

Still, though, split works on characters and not globs in the substution itself, from the documentation:

s:string:
    Force field splitting (see the option SH_WORD_SPLIT) at the separator string.

So, it doesn't look like you can split on a pattern. The ~ character modifies the interpretation of the string to be split.

Also, from the same pattern expansion documentation you reference, it continutes:

Compare with a ~ outside parentheses, which forces the entire substituted string to be treated as a pattern. [[ "?" = ${(~j.|.)array} ]] with the EXTENDED_GLOB option set succeeds if and only if $array contains the string ‘?’ as an element. The argument may be repeated to toggle the behaviour; its effect only lasts to the end of the parenthesised group.

The difference between ${(~j.|.)array} and ${(j.|.)~array} is that the former treats the values inarray as global, and the latter treats the result as a glob.

See also:

${~spec} Turn on the GLOB_SUBST option for the evaluation of spec; if the ‘~’ is doubled, turn it off. When this option is set, the string resulting from the expansion will be interpreted as a pattern anywhere that is possible, such as in filename expansion and filename generation and pattern-matching contexts like the right hand side of the ‘=’ and ‘!=’ operators in conditions.

Here is a demo that shows the differences:

$ array=("foo???bar???baz" "foo???bar???buz")
$ [[ "foo___bar___baz" = ${(~j.|.)array} ]] && echo true || echo false
false
$ [[ "foo___bar___baz" = ${(j.|.)~array} ]] && echo true || echo false
true

And for completeness:

$ [[ "foo___bar___baz" = ${(~j.|.)~array} ]] && echo true || echo false
true

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...