12

I have a script I'm running to fix file ownership and permissions after an rsync. Questions of the optimal way to do my task aside, I wonder, is there a way to run chmod and chown at the same time?

In the current iteration of my script, I'm finding files twice.

find /var/www/mysite -exec chown www-data:www-data {} \; find /var/www/mysite -type f -exec chmod 775 {} \; 

I thought it would be nice if I could change both the permissions and owner/group with a single command. After some googling, I was surprised to learn that such a command, argument, or option doesn't exist.

Can I change both the permissions and ownership at the same time, to avoid finding each file twice?

Edit A community edit or post or something suggested that this question is a duplicate of "Change all folder permissions with 1 command". This question is different because it asks about changing both permissions and ownership at the same time, not just permissions.

12
  • 7
    you really, really, really don't want all your web files to be 775. Commented Apr 16, 2020 at 14:07
  • If your files have permission 755 be aware that a user on the same server (for instance shared hosting) can edit your files. It is better to use 755 for folder and 644 for files. Commented Apr 16, 2020 at 15:57
  • 1
    @GuyT: That is incorrect. 755 is rwxr-xr-x. It doesn't allow modification by anyone other than the owner. Commented Apr 16, 2020 at 17:14
  • 1
    @OlivierDulac this is for a local development instance inside a vbox vm Commented Apr 16, 2020 at 17:47
  • 1
    Does this answer your question? Change all folder permissions with 1 command Commented Apr 18, 2020 at 5:34

4 Answers 4

18

You can pass multiple exec commands:

find /var/www/mysite -exec chown www-data:www-data {} \; \ -type f -exec chmod 775 {} \; 
2
  • 8
    @user394 No, you can't, because each predicate (-type, -exec etc) is a test, and there's a logical AND between them (implicitly). You could work out the logic with OR (-o) and parentheses. Possibly something like \( -type d -exec chmod g+s {} \; \) -o \( -type f -exec chmod 775 {} \; \) etc. Commented Apr 15, 2020 at 19:17
  • 8
    @user394 In Jesse's code, chmod will only ever be executed for regular files (-type f) that the preceding chown was successful for. If the chown fails, the -type f etc. won't happen, and the next thing found will be considered instead. (that's how -exec can be seen as a test). Commented Apr 15, 2020 at 19:19
10

You could add the equivalent options to your rsync command:

rsync <your_options> --chown www-data:www-data --chmod=F775 <source> <destination> 

You can use prefix F in --chmod for files and D for directories.

1
  • I'm guessing the user UID database is not the same between both hosts. This would be one reason for using ldap or another central repository of UID info on a network. Commented Apr 16, 2020 at 2:34
3

For completeness xargs can do all sorts of interesting things in pipelines too.

find . -type f -print0 | xargs -0 -I VAR -- sh -c 'chmod 775 "VAR" && chown www-data:www-data "VAR" ' 

This produces a stream of filenames (not directory names) with nulls for separators, so deals with spaces in filenames.

-0 tells xargs to separate inputs on the null.
-I VAR says to use VAR as the "variable name" rather than {}
-- and everything after it is what to run for each line

This may be more readable, but it will be starting a new shell for each run of the double-barrelled command.

1
  • Never embed the text substituted by xargs inside the shell code (or code to any interpreter for that matters), as that makes it a code injection vulnerability. Use -exec sh -c 'for file do chmod 775 "$file" && chown www-data: "$file"; done' sh {} +. xargs is rarely useful to process find's output. Commented Apr 16, 2020 at 14:33
2

If you're worried about finding each file twice, you may also want to worry about the number of forks that running a combination of commands via xargs would produce.

People often forget that perl has equivalents for many of these basic shell commands. Here's one way I'd do this (tested):

find | perl -lne 'chown 1001,1001, $_; -d $_ ? chmod(0755, $_) : chmod(0644, $_)' 

You'd have to use the numeric uid, gid, and mode, but think about this: this does not fork at all (beyond the one find and the one perl).

And you don't need to know a lot of perl to grok it either.

The chown and chmod are clear enough. -d is the same as in shell's [ or the test command, and the ternary operator foo ? bar : baz is not unique to perl. (As a bonus, you get to use different modes for directories and files.)

In fact it's the options that may need explanation if you're new to perl. A bit simplified:

  • the -n means "run this once for each line of STDIN, setting $_ to the line"
  • -l makes the line feed at the end of each line disappear (because otherwise that becomes part of $_)
  • -e is familiar to people who know sed; it says this is the expression to evaluate/run.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.