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" [1]. This question is different because it asks about changing both permissions and ownership at the same time, not just permissions.
You can pass multiple exec commands:
find /var/www/mysite -exec chown www-data:www-data {} \; \
-type f -exec chmod 775 {} \;
-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. - Kusalananda
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). - Kusalananda
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.
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.
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. - Stéphane Chazelas
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:
-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.
755isrwxr-xr-x. It doesn't allow modification by anyone other than the owner. - R.. GitHub STOP HELPING ICE-print0to the end and pipe the filenames into a single instance of Perl which can do the thechmodandchownas simple library calls without starting new processes. - Mark Setchellfind— which is what you’re asking for. (OK, not exactly the same, but it’s more advanced than jesse_b’s answer to your question, so you should have been able to adapt it.) - G-Man Says 'Reinstate Monica'