11

While using fish as my shell, i'm trying to set permissions on a bunch of c source files in current dir with

find . -type f -name "*.c" -exec chmod 644 {} +;

I get an error

find: missing argument to `-exec'

or

find . -type f -name "*.c" -exec chmod 644 {} \;

I get an error

chmod: cannot access '': No such file or directory

What's wrong?

Rahul
  • 13,589
Darc Nawg
  • 229
  • 1
    try quoting the {} with "{}" or '{}' i.e. find . -type f -name "*.c" -exec chmod 644 '{}' + – the_velour_fog Aug 04 '16 at 09:16
  • why the ; after + ? – Anthon Aug 04 '16 at 09:20
  • @Anthon, if you only just arrived at this question, originally the OP posted the question without the ;, ie. .... chmod 644 {} + . Then Thomas Dickey posted his answer, then the OP edited to add the ; as you see it now. I know that still doesn't explain properly why the OP did it though... – the_velour_fog Aug 04 '16 at 09:22
  • @the_velour_fog I reviewed the question from the first post queue, you don't see the answers there. The +; looks someone mixed up things from find's man page. – Anthon Aug 04 '16 at 09:25
  • The semicolon at the end of the first command is just unnecessary but doesn't matter, both bash and fish eat it before passing the arguments to find – ilkkachu Aug 04 '16 at 09:31
  • @ilkkachu Confirmed. Semicolon is redundant. – Darc Nawg Aug 04 '16 at 09:44

3 Answers3

19

fish happens to be one of the few shells where that {} needs to be quoted.

So, with that shell, you need:

find . -type f -name '*.c' -exec chmod 644 '{}' +

When not quoted, {} expands to an empty argument, so the command becomes the same as:

find . -type f -name '*.c' -exec chmod 644 '' +

And find complains about the missing {} (or ; as + is only recognised as the -exec terminator when following {}).

With most other shells, you don't need the quotes around {}.

3

Your examples miss the expected trailing semicolon:

find . -type f -name "*.c" -exec chmod 644 {} \;

After revising the question, it is "fish" shell. This is a known issue which can be worked around using quoting as @rahul noticed. However, the escaping suggested does not work for my configuration: single quoting does:

find . -type f -name "*.c" -exec chmod 644 '{}' \;
find . -type f -name "*.c" -exec chmod 644 '{}' +

What does happen (if one types the characters rather than cut/paste) is that on trying to edit the command-line to escape the curly braces, fish gets confused and cannot proceed. Here's a screenshot just after inserting the backslashes (no point in trying to cut/paste that):

editing the command-line

and then pressing return:

just press return

So no, fish doesn't really work with escaped curly braces. It only pretends to do that. Continuing to press enter gives a conclusive demo:

q.e.d.

Further reading:

Thomas Dickey
  • 76,765
  • but the what is wrong with the first expression ? – Rahul Aug 04 '16 at 09:12
  • 1
    I don't get an error with that. OP may be confused with the latter. – Thomas Dickey Aug 04 '16 at 09:13
  • With the above example I get chmod: cannot access '': No such file or directory – Darc Nawg Aug 04 '16 at 09:14
  • I am using fish shell. – Darc Nawg Aug 04 '16 at 09:15
  • Looks like fish is pre-interpreting the find command (and only knows about the second case). I'll take a quick look for source to clarify this. – Thomas Dickey Aug 04 '16 at 09:18
  • backslash-escaping the braces works fine in fish (v. 1.23.1, from Debian) if you enter the characters from left to right, that is backslash-brace-backslash-brace. Entering the braces first and going back to enter the backslashes doesn't, since fish seems to consider the backslash as quoting the next character entered (like ^V in bash), not as a simple character. So, backslash-enter adds a newline to the command line, instead of submitting the command line to be executed. – ilkkachu Aug 04 '16 at 21:51
3

{ and } have special meanings in fish. They need to be escaped in order to work with find, for example:

find . -type f -name "*.c" -exec chmod 644 \{\} \;

Or you would have to quote {} like,

find . -type f -name "*.c" -exec chmod 644 '{}' \;
Rahul
  • 13,589