5 ms·
Discussed extensively last year: https://news.ycombinator.com/item?id=26776956 https://news.ycombinator.com/item?id=26776956 One key point there is that it’s s
by chrismorgan 4y ago
Discussed extensively last year: https://news.ycombinator.com/item?id=26776956 https://news.ycombinator.com/item?id=26776956
One key point there is that it’s still easy to trigger cases where you need this hack, because the cleverness is only stable for up to four arguments; so and, or, and parentheses all exceed the point of safety.
- topspin 4y agoSo keep doing it despite whomever it annoys. Also, throw some superfluous cats in to annoy them more.
- amluto 4y agoAll the cleverness and precedence rules in the article seem to ignore the fundamental problem: the test and [ binaries are regular programs, and they cannot distinguish between an argument that looks like “(“ and (. In both cases, they get the string “(“ in argv. So x serves as a form of quoting. If I were to potentially invoke a binary to evaluate an expression, I would quote any string-like variable (using the x hack) and I would do my best to avoid numeric comparisons. (Or use built-in shell features where the shell knows which values are quoted.)
- rascul 4y ago> All the cleverness and precedence rules in the article seem to ignore the fundamental problem: the test and [ binaries are regular programs, and they cannot distinguish between an argument that looks like “(“ and (. In both cases, they get the string “(“ in argv. Regular programs can make that distinction with no problem, the mistake is thinking that the outer quotes are part of the argument. In the context of the POSIX style shell, they're not, they signify that what is enclosed is one argument. So you'll have to use one of the various mechanisms the shell provides to indicate that you want to pass the quotes as an argument.
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]
- Sophira 4y agoI'm not the person you replied to, but I was unaware of the fact that it was possible to tell the difference between the arguments ( and "(" in a regular program. May I ask how? My understanding was that the shell stripped the quotes before it ever reached the program (and that the only method of bypassing that would have been to escape the quotes or enclose the argument in single quotes, for example).
- rascul 4y ago$ echo "(" '"("' ( "(" "(" the argument is ( '"("' the argument is "(" When you inspect argv in your program, you will see the argument including the quotes in the second example.
- Sophira 4y agoAh yes, that's what I meant by putting the argument in single quotes. I thought you were saying that the statement 'In both cases, they get the string “(“ in argv.' wasn't necessarily true. My apologies!
- a1369209993 4y ago> the mistake is thinking that the outer quotes are part of the argument. > you want to pass the quotes as an argument. Yes, that's the point - in the context of /bin/[, 'x' is the quote; that's how you quote string arguments to [. (Yes, it's ad hoc as hell; that's fairly par for the course for unix.)
- rascul 4y agoI don't get what you're saying. x isn't the quote, it's made to be part of the string being matched, to catch edge cases. You don't need to understand quoting rules to pass 'x' as an argument as you would '"("'.
- dwheeler 4y agoNo need for that. In shell the preferred form for "and" and "or" is outside the test, and you always quote anything with a variable reference, like this: if [ "$x" = "foo" ] && [ -z "$empty" ] ; then echo "Worked" fi I think the "&&" is easier to read, as well as completely eliminating the problem.
- dataflow 4y agoThat was also discussed in the thread: https://news.ycombinator.com/item?id=26778153 https://news.ycombinator.com/item?id=26778153
- dezgeg 4y agoNot to mention this syntax short-circuits, which is usually desirable.
- cduzz 4y agoOh, shell pet peev #301; shell has "case" as part of the language, and case is way better at string evaluation that the "test" built in case "$x" in (foo) echo Worked ;; esac
- cduzz 4y agoSorry I should have elaborated... "test" or it's other name "[" is a program or a built in. It takes a parameter to tell it what to do. It can evaluate strings, but it can also look at the file system or do a little numeric evaluation. Newer versions in gnuland may even be able to tell you if a unicode glyph is left to right or right to left. It does all this using - or -- prefixed options. test -f /dev/sda will fail (it's a block device not a file) test -f /dev will fail (it's a directory) test -h /usr/bin/[ will succeed on some unices (it's a symbolic link to /usr/bin/test) So -- what does [ "$x" = foo ] (or, more clearly, test "$x" = foo) do if the variable x happens to have "-f" in it? The shell will interpret the " and the $x and hand to the builtin this: [ -x = foo ] (or test -x = foo) Now, the human is waving their hands "that's not what I meant no fair!" but a contract's a contract -- test might look around for a directory entry in the current directory and not find a file named =, or hey, it might, because = is a fair directory entry name. It may then poke at that associated inode and find that = is in fact a file, and return a 0 in the exit status. Or it may say "hey, file existence testing has one paramater and you gave me a filename, =, and another file, foo, what gives? Regardless -- you've got no control over the contents of variable x; that's why you're asking test to look into it for you. You can blorp a thing in front of it to protect yourself from this case, but that's just silly. Test can't do any regular expressions and it does all sorts of other things, so it's taking parameters, it's just a mess. Just use case $x in ... esac. Test is great for other stuff, but string evaluation? It wasn't ever good for it.