Paul L. Snyder on 20 Sep 2010 12:01:14 -0700 |
On Mon, 20 Sep 2010, Gavin W. Burris wrote: > Passing variables to sed bit me recently. I finally figured it out, > proper use of the quotes and escapes, which seems painfully obvious now: > http://idolinux.blogspot.com/2010/08/passing-variables-to-sed.html sh (and bash) word-splitting conventions are atrocious: word splitting happens after expansion (brace, parameter, and arithmetic) and substitution (process and command), but before globbing. During word splitting, the command-line is divided up based on the delimiters in the IFS environment variable (by default: space, tab, and newline). bash% f() { echo "First: -$1- Second: -$2-"; } bash% A="1 2" bash% f $A First: -1- Second: -2- Word splitting applies only to the text resulting from command substitution, parameter expansion, or arithmetic expansion. This seems like it might be vaguely sane until you start tossing quotes into the mix. bash% B="\"3 4\"" bash% f $B First: -"3- Second: -4"- Yech! Quote removal happens after all expansions, but text that is the result of an expansion or substitution will not have quotes removed, giving us the hideous result above. zsh breaks with traditional Bourne shell word-splitting, and introduces a number of features that make dealing with this sort of thing much easier. zsh% setopt NO_SH_WORD_SPLIT # The zsh default zsh% g() { echo "First: -$1- Second: -$2-"; } zsh% A="1 2" zsh% f $A First: -1 2- Second: -- zsh will NOT split on whitespace by default. As with bash, quotes are not removed: zsh% B="\"3 4\"" zsh% g $B First: -"3 4"- Second: -- You get fine-grained control over what you want to happen with your expansions. Rather than having to lobotomize your shell by setting SH_WORD_SPLIT, you can split a single expansion: zsh% g ${=A} First: -1- Second: -2- As with sh/bash, splitting is based on the contents of IFS. The braces are usually optional but help keep thing clear when the parsing gets hairy. The result with the quoted contents of B still isn't very nice, though: zsh% g ${=B} First: -"3- Second: -4"- Fortunately, we can control this with the parameter expansion flag 'Q', which removes a level of quoting when the envvar is expanded (note the nested expansion, since we're splitting the result of the first expansion. zsh% g ${=${(Q)B}} First: -3- Second: -4- Or, we could have quoting go the other way, using 'q', which adds a level of quoting (using backquotes): zsh% g ${(q)A} First: -3\ 4- Second: -- We can easily split on a different character: zsh% C="5 6/7" zsh% g ${(s:/:)C} First: -5 6- Second: -7- And for a last example, we can perform sensible splitting based on the quoting inside the envvar: zsh% D="8 \"9 0\"" zsh% g ${(z)D} First: -8- Second: -"9 0"- zsh% g ${(Q)${(z)D} First: -9' Second: -9 0- Here endeth today's zsh evangelism. It won't do you any good if you have to script for bash, but if you've got enough control over your target environment that you can ensure zsh is present, it can make your life much easier. Paul ___________________________________________________________________________ Philadelphia Linux Users Group -- http://www.phillylinux.org Announcements - http://lists.phillylinux.org/mailman/listinfo/plug-announce General Discussion -- http://lists.phillylinux.org/mailman/listinfo/plug
|
|