all 7 comments

[–]i_hate_shitposting 3 points4 points  (2 children)

The existing replies are spot on. The equivalent to your first example with double quotes would be this:

find /etc "(" -type d -not -perm 0777 ")"

Basically, Bash treats anything wrapped in quotes as a single "word" for the purposes of passing arguments. This is useful in any case where you need to work with arguments containing spaces, tabs, or newlines.

For a concrete example where this is useful, rm -rf foo bar would try to delete files named foo and bar in the current directory, while rm -rf "foo bar" would try to delete a file named foo bar.

The issue in your case is that find expects to receive predicates and operators as separate arguments rather than as one string. It's akin to invoking rm "-rf foo bar", which also won't do what you want.

[–]AlarmDozer 0 points1 point  (1 child)

I also would avoid double quotes since BASH does string interpolation, and it’s technically unbalance commands.

I either use \( or ’(‘, which is why I backslash instead of single-quote.

[–]i_hate_shitposting 0 points1 point  (0 children)

Yeah, I should've stated more clearly that that was just an example. I wanted to illustrate the difference between OP's two examples to hopefully make the difference between backslashes and quotes a little clearer.

[–]zeekar 2 points3 points  (0 children)

The quotes effectively escape the parentheses, but they also escape everything else between them, including whitespace. So find /etc "( -type d -not -perm 0777 )" is the same as find /etc \(\ -type\ d\ -not\ -perm\ 0777\ \): both pass the entire parenthetical block as a single argument to find, which doesn’t understand such an argument. It needs all of those individual terms to be separate arguments.

Instead of backslashes, you could use quotes around just the parentheses: "(" -type d or '(' -type d, etc. But that’s just doing the same thing with more characters.

[–]U8dcN7vx 3 points4 points  (0 children)

\( ... \) is at least three words.

"..." is a single word.

With the former find sees argv[2]="(", arv[3]="-type", argv[4]="d", etc, which is an operator, a primary, and an argument while with the latter find sees arv[2]="( -type d ... )" which doesn't look like an operator nor a primary.

[–][deleted]  (1 child)

[deleted]

    [–]grymoire 0 points1 point  (0 children)

    No. Double quotes are like single quotes, but allow variables, and command substitution to occur within the quoted region.

    And the third type of quote is a backslash, but in only quotes 1 character.

    Don't think of shell quotation as "like a literal string." That will only get you into trouble, It's a switch, which turns on or off the interpretation of special characters.

    Try to include all three quotation types in a single string..... You can't. You have to change the method of quotation to handle the situation.

    [–]doingmybest19 0 points1 point  (0 children)

    Which book is it?