Re: manpage note? weird strings that appear to be equal but create haywire comparisons?
Chet Ramey wrote: Note that [[ and [ return different results when the vars are unquoted. Yes. There are two differences. First, the operands in [[ do not undergo all word expansions. The arguments to [, since it's a builtin, do. That doesn't really matter to this example, but it's worth noting. Second, the = and != operators in [[ perform pattern matching (= is the same as ==). You have to quote the rhs to force string matching. In pattern matching, the backslash has a special meaning: it quotes the next character. --- That makes sense -- but I was attributing it to the backslash being within double quotes vs. single quotes, paralleling the expansion of variables. *sigh* So when $v is unquoted, the pattern matcher treats the pairs of backslashes in $v as one backslash quoting another, and the strings match. When using [, bash does straight string comparison, and the strings are not identical. --- That, I did not know. I thought it had to do with whether or not you enclosed the string in single quotes or double quotes there, too... I think I'm getting string rules from various languages confused at times...*sigh*
Re: manpage note? weird strings that appear to be equal but create haywire comparisons?
Linda Walsh wrote: > But then I tested equality on the strings and that's the confusing > part. I have an idea of what's going on but boy do string compares look > confused. They perform same on cygwin > (bashv=3.2.49(22)-release (i686-pc-cygwin)) and Suse11.1: > (bashv=3.2.39(1)-release (x86_64-suse-linux-gnu) > > Using the simple form: >if ; then echo = "True" else echo False ; fi > I get: > for c=C:\Windows\System32, and v=C:\Windows\System32 (If you use the assignments above, v is actually assigned the string C:\\Windows\\System32) > expr = [[ "$c" = "$v" ]] : "False" > expr = [ "$c" = "$v" ]: "False" > expr = [[ "$c" != "$v" ]] : "True" > expr = [ "$c" != "$v" ] : "True" > expr = [[ $c = $v ]] : "True" > expr = [ $c = $v ]: "False" > expr = [[ $c != $v ]] : "False" > expr = [ $c != $v ] : "True" > > Note that [[ and [ return different results when the vars are unquoted. Yes. There are two differences. First, the operands in [[ do not undergo all word expansions. The arguments to [, since it's a builtin, do. That doesn't really matter to this example, but it's worth noting. Second, the = and != operators in [[ perform pattern matching (= is the same as ==). You have to quote the rhs to force string matching. In pattern matching, the backslash has a special meaning: it quotes the next character. So when $v is unquoted, the pattern matcher treats the pairs of backslashes in $v as one backslash quoting another, and the strings match. When using [, bash does straight string comparison, and the strings are not identical. Chet -- ``The lyf so short, the craft so long to lerne.'' - Chaucer ``Ars longa, vita brevis'' - Hippocrates Chet Ramey, ITS, CWRUc...@case.eduhttp://cnswww.cns.cwru.edu/~chet/
Re: manpage note? weird strings that appear to be equal but create haywire comparisons?
Greg Wooledge wrote: On Wed, Aug 26, 2009 at 02:45:39AM -0700, Linda Walsh wrote: I had a var 'c' set to (no quotes in the var): 'C\windows\system32' How did you assign this value? Did you read it from a file? Did you type a specific bash command? -- I typed it in at the prompt as: # c='C:\Windows\System32' # echo $v |hexdump -C 43 3a 5c 77 69 6e 64 6f 77 73 5c 73 79 73 74 65 This isn't trustworthy, because you did not quote $v. --- Didn't bother quoting it because quoted or unquoted, it gave the same output in this case, but in general, you are right. # printf -v v "%q" $c Same problem here -- unquoted $c means you're not necessarily getting the variable's exact contents. --- Again, same response as above (made no diff in this case, but in general, quoting "is good" :-). for c=C:\Windows\System32, and v=C:\Windows\System32 Even with the unquoted $v here, you should have seen the double backslashes: $ echo $v C:\\Windows\\System32 So your output doesn't match your script. Something strange is afoot. --- Oh...yeah, forgot about my 'xpg_echo' option...that explains echo but that wasn't my "ponderance", -- I knew about the internal contents varying (from tracing the script during debugging, ie '+x'). Note, I'm not saying 'echo' has a bug or there is a problem with how bash stores things internally. Remember, I did mention that I "an idea" of what was going on with the 'echo' stuff -- meaning (one is «'C:\windows\system32'» and the other is «C:\\windows\\system32»), but my email header was to indicate (perhaps unclearly) whether or not such behavior was worthy of a ‘manpage note’ for the unwary -- especially the difference in the test ops mentioned below, which, I'm not entirely clear, yet, about why they give the different results they give. I understand why the quoted versions "$c" = "$v" both say 'different', (their internal representation is different). But the unquoted differences appear a bit odd. I can make guesses, and rationalizations, but those would be those. Since we don't really know WHAT your variables contain, any analysis of that output is a waste of time, --- Sure ya do! I just typed: # c='C:\windows\system32' although Pierre gave some decent pointers about the general difference between [ and [[. --- Indeed...(and verily!) On Wed, Aug 26, 2009 at 01:17:07PM +0300, Pierre Gaston wrote: Now I'm not too sure why var='"*'"; [[ \* = $var ]] is false while var='\*' [[ \* = $var ]] is true Assuming the first part was supposed to be var='"*"' ... --- Oops...sorry, my bad. I really miscommunicated. What I meant that I found it odd/confusing that the True/False indicators for these ops were different: for (really, when echo'ing correctly ('just ought to have used printf!') c=«'C:\windows\system32'» and v=«C:\\windows\System32» «[[ $c = $v ]]» and «[ $c = $v ]» (and complements) «[[ $c != $v ]]» and «[ $c != $v ]» My guess? the [[/]] op dequotes the internally stored form, which in one case, I think, may contain the actual single quote char?, but then the equality test strips the quotes for comparison? Whereas the [] ops know about internal forms and are comparing the strings: "'C:\windows\system32'" w/ "C:\windows\system32", so it's not stripping the "SQ"s off the 1st string? Just a guess... Linda
Re: manpage note? weird strings that appear to be equal but create haywire comparisons?
On Wed, Aug 26, 2009 at 4:19 PM, Greg Wooledge wrote: > Assuming the first part was supposed to be var='"*"' ... > yup > > The bash command [[ \* = $var ]] returns true if $var contains a glob > pattern against which a literal asterisk * can be matched. (By the way, > you don't need the \ there. No glob expansion is done inside [[...]] so > you could use a plain * on the left hand side.) > > If $var contains \* then the match is successful. \* as a glob describes > a literal asterisk, which is what we're trying to match. > $ var='\*'; [[ * = $var ]]; echo $? > 0 > > If $var contains "*" (double quote, asterisk, double quote), then > the double quotes are considered part of the glob pattern. A bare > asterisk won't match that glob, because it doesn't have double quotes > attached to it. > > imadev:~$ var='"*"'; [[ * = $var ]]; echo $? > 1 > > It only matches double quote, asterisk, double quote: > > $ var='"*"'; [[ \"*\" = $var ]]; echo $? > 0 Thanks, I agree with that, I'm sorry I should have been more explicit, what was not clear to me was where this special role of the \ is explained, Because if you use literals [[ something = \* ]] is the same as [[ something = "*" ]] I found my explanation in the manual under "Pattern Matching": "A backslash escapes the following character; the escaping backslash is discarded when matching." PS, for Linda, http://mywiki.wooledge.org/BashFAQ/031 has more information about the differences between [[ and [
Re: manpage note? weird strings that appear to be equal but create haywire comparisons?
On Wed, Aug 26, 2009 at 04:36:42PM +0300, Pierre Gaston wrote: > Thanks, I agree with that, I'm sorry I should have been more explicit, > what was not clear to me was where this special role of the \ is explained, > Because if you use literals [[ something = \* ]] is the same as [[ > something = "*" ]] The difference is whether it's being read by the parser or not. When you put the quotes directly into the command like that, the parser sees them, acknowledges their function, and removes them. When they're part of a variable substitution, they're not removed.
Re: manpage note? weird strings that appear to be equal but create haywire comparisons?
On Wed, Aug 26, 2009 at 02:45:39AM -0700, Linda Walsh wrote: > I was scripting and fixing some permissions in Win, > and had a var 'c' set to (no quotes in the var): 'C\windows\system32' How did you assign this value? Did you read it from a file? Did you type a specific bash command? > # echo $v |hexdump -C > 43 3a 5c 77 69 6e 64 6f 77 73 5c 73 79 73 74 65 This isn't trustworthy, because you did not quote $v. The variable might have leading or trailing whitespace, which the unquoted parameter expansion would remove before echo gets it. There's also glob expansion to worry about. Your variable might contain C:\windows\sy?tem3* for all we know. > # printf -v v "%q" $c Same problem here -- unquoted $c means you're not necessarily getting the variable's exact contents. > # echo $v |hexdump -C And here. > TestProg: (interactive shell...) > > { > c='C:\Windows\System32' > printf -v v "%q" "$c" Finally, quotes! Here's what your v variable should contain after that: $ c='C:\Windows\System32'; printf -v v %q "$c"; echo "$v" C:\\Windows\\System32 But your test script says: > export c v > echo for c=$c, and v=$v And your alleged output says: > for c=C:\Windows\System32, and v=C:\Windows\System32 Even with the unquoted $v here, you should have seen the double backslashes: $ echo $v C:\\Windows\\System32 So your output doesn't match your script. Something strange is afoot. > Note that [[ and [ return different results when the vars are unquoted. Since we don't really know WHAT your variables contain, any analysis of that output is a waste of time, although Pierre gave some decent pointers about the general difference between [ and [[. On Wed, Aug 26, 2009 at 01:17:07PM +0300, Pierre Gaston wrote: > Now I'm not too sure why var='"*'"; [[ \* = $var ]] is false while > var='\*' [[ \* = $var ]] is true Assuming the first part was supposed to be var='"*"' ... The bash command [[ \* = $var ]] returns true if $var contains a glob pattern against which a literal asterisk * can be matched. (By the way, you don't need the \ there. No glob expansion is done inside [[...]] so you could use a plain * on the left hand side.) If $var contains \* then the match is successful. \* as a glob describes a literal asterisk, which is what we're trying to match. $ var='\*'; [[ * = $var ]]; echo $? 0 If $var contains "*" (double quote, asterisk, double quote), then the double quotes are considered part of the glob pattern. A bare asterisk won't match that glob, because it doesn't have double quotes attached to it. imadev:~$ var='"*"'; [[ * = $var ]]; echo $? 1 It only matches double quote, asterisk, double quote: $ var='"*"'; [[ \"*\" = $var ]]; echo $? 0
Re: manpage note? weird strings that appear to be equal but create haywire comparisons?
On Wed, Aug 26, 2009 at 12:45 PM, Linda Walsh wrote: > I was scripting and fixing some permissions in Win, > and had a var 'c' set to (no quotes in the var): 'C\windows\system32' > I first bumped into this using the printf -v var "%q" feature, where > I expected it to doublequote the back slashes. But instead, it looked > like I got the same value assigned to v as though I'd just done a 'c=v' > > # printf -v v "%q" $c > # echo $v |hexdump -C > 43 3a 5c 77 69 6e 64 6f 77 73 5c 73 79 73 74 65 > |C:\windows\syste| > 0010 6d 33 32 0a 0014 here is what I get: $ echo $v | hexdump -C 43 3a 5c 5c 57 69 6e 64 6f 77 73 5c 5c 53 79 73 |C:\\Windows\\Sys| 0010 74 65 6d 33 32 0a |tem32.| 0016 if you run your test prog after setting "set -x" (or run it with bash -x testprog) you will see the double \\. > Note that [[ and [ return different results when the vars are unquoted. The bash keyword [[ and [ are different in several ways (word splitting does not occur for instance) in your example the difference is that = inside [[ ]] does pattern matching. (note that in the case of bash [ is also a builtin) * If you quote the right hand side variable, then the expression is taken literally, the test becomes: [[ 'C:\Windows\System32' != 'C:\\Windows\\System32' ]] and it's false. * If you don't quote the right hand side variable, then the expression is taken as a pattern, the test becomes [[ 'C:\Windows\System32' != C:\\Windows\\System32 ]] in this glob the the \ is consider as escaping the other \ and the 2 variables are equal. That explaine the differences. Now I'm not too sure why var='"*'"; [[ \* = $var ]] is false while var='\*' [[ \* = $var ]] is true
manpage note? weird strings that appear to be equal but create haywire comparisons?
I was scripting and fixing some permissions in Win, and had a var 'c' set to (no quotes in the var): 'C\windows\system32' # echo $v |hexdump -C 43 3a 5c 77 69 6e 64 6f 77 73 5c 73 79 73 74 65 |C:\windows\syste| 0010 6d 33 32 0a |m32.| 0014 I first bumped into this using the printf -v var "%q" feature, where I expected it to doublequote the back slashes. But instead, it looked like I got the same value assigned to v as though I'd just done a 'c=v' # printf -v v "%q" $c # echo $v |hexdump -C 43 3a 5c 77 69 6e 64 6f 77 73 5c 73 79 73 74 65 |C:\windows\syste| 0010 6d 33 32 0a 0014 But then I tested equality on the strings and that's the confusing part. I have an idea of what's going on but boy do string compares look confused. They perform same on cygwin (bashv=3.2.49(22)-release (i686-pc-cygwin)) and Suse11.1: (bashv=3.2.39(1)-release (x86_64-suse-linux-gnu) Using the simple form: if ; then echo = "True" else echo False ; fi I get: for c=C:\Windows\System32, and v=C:\Windows\System32 expr = [[ "$c" = "$v" ]] : "False" expr = [ "$c" = "$v" ]: "False" expr = [[ "$c" != "$v" ]] : "True" expr = [ "$c" != "$v" ] : "True" expr = [[ $c = $v ]] : "True" expr = [ $c = $v ]: "False" expr = [[ $c != $v ]] : "False" expr = [ $c != $v ] : "True" Note that [[ and [ return different results when the vars are unquoted. TestProg: (interactive shell...) { c='C:\Windows\System32' printf -v v "%q" "$c" export c v echo for c=$c, and v=$v for ((qq=2;qq>=0;qq-=2)); do if ((qq==2)); then q='"'; else q=''; fi for test in '=' '!=' ; do for ((op=1;op<=2;++op)) ; do if (($op==1)) ; then to="[["; tc="]]" ; else to="["; tc="]"; fi expr=$(printf "%s %s%s%s %s %s%s%s %s" "$to" "$q" '$c' "$q" "$test" "$q" '$v' "$q" "$tc") printf "expr = %-18s : \"" "$expr" if eval $expr ; then echo True\" ; else echo False\" ; fi done done done; # vim:et:ts=1:sw=1 } -- I sorta understand why they wouldn't be entirely equal, but having the built-in treat them differently than the single brackets is a bit surprising.. If I put them outside