find . .[^.]* -type f -print0 | xargs -0 sudo chmod 664; does not work

9,702

Solution 1

find will not skip dot files, so .[^.]* (.[!.]* in standard syntax) is redundant and will cause those files to be processed twice (and directories to be descended twice). (also note that .[!.]* misses files called ..foo for instance).

However, find by default, does not descend into symlinks to directories. So if media is a symlink to an area outside ., files in there will not be processed.

You can use -L to tell find to follow symlinks. Note however that it will also cause chmod to be called for files that are symlinks to regular files.

sudo find -L . -type f -exec chmod 664 {} +

Or with some find/xargs implementations:

find -L . -type f -print0 | sudo xargs -r0 chmod 664

Contrary to what has been said here, xargs and find -exec + will take care of splitting the list of arguments to chmod if it's too big.

However it should be noted that the limit we're talking about here is the cumulative size of the arguments and environment passed to a command (to the execve(2) system call).

find and xargs will make sure that that limit is not reached (and leave a little margin), but if you run it as:

find . -type f -print0 | xargs -r0 sudo chmod 644

instead of:

find . -type f -print0 | sudo xargs -r0 chmod 644

The sudo command (called by xargs) will receive all the arguments to pass to chmod and will also pass a SUDO_COMMAND environment variable to chmod that contains the list of files another time, so it will about double the size of arg+env passed to chmod and would explain why the limit is exceeded.

Solution 2

It is possible that the argument string you are passing to chmod is too long. You have a few options here.

Using find's exec option

When used with a semicolon, find's exec option will run a command once per file it finds: find . .[^.]* -type f -exec sudo chmod 664 -- '{}' \;.

Limiting the number of arguments with xargs

xargs -n5 will pass a maximum of five arguments per command instance. You could also use the -s flag to limit the number of characters passed to the command: xargs -s4096. You can increase the limits for better performance; just be careful not to go to high.

Recursive chmod

chmod has a recursive option. This will affect directories as well as files though. If you are concerned about unsetting the execution bits on directories, you can run chmod -R +X (note the uppercase X) after you run your first command to recursively set the execution bits of your directories.

Share:
9,702

Related videos on Youtube

clime
Author by

clime

Updated on September 18, 2022

Comments

  • clime
    clime over 1 year

    I am using this command to set permissions for files recursively

    clime@vm6879 /srv/www-php/steeltrading $ find . .[^.]* -type f -print0 | xargs -0 sudo chmod 664
    

    But after executing that command permissions of some files (most of them) are still not changed.

    clime@vm6879 /srv/www-php/steeltrading $ ll media/xmlconnect/system/ok.gif 
    -rwxrwxr-x. 1 www www 295 Jul  5  2012 media/xmlconnect/system/ok.gif
    

    If I run just find to look if the file is in the list, I can see it:

    clime@vm6879 /srv/www-php/steeltrading $ find . .[^.]* -type f | grep ok.gif
    ./media/xmlconnect/custom/ok.gif
    ./media/xmlconnect/original/ok.gif
    ./media/xmlconnect/system/ok.gif
    

    If I move into directory media a run the command again, the file finally gets the right permissions.

    clime@vm6879 /srv/www-php/steeltrading $ cd media/
    clime@vm6879 /srv/www-php/steeltrading/media $ find . .[^.]* -type f -print0 | xargs -0 sudo chmod 664
    clime@vm6879 /srv/www-php/steeltrading/media $ ll xmlconnect/system/ok.gif 
    -rw-rw-r--. 1 www www 295 Jul  5  2012 xmlconnect/system/ok.gif
    

    It looks like sudo chmod 664 is not performed for some files and it seems that xargs is the problem but It is strange (no error messages). Any idea what might be the cause, please?

    EDIT: Or maybe piping lots of data might be a problem?

    • Admin
      Admin about 11 years
      Try using find's -exec option: find . .[^.]* -type f -exec sudo chmod 664 -- '{}' \;.
    • Admin
      Admin about 11 years
      You might be passing too many arguments to chmod. Try xargs -n5 to only allow 5 arguments per command.
    • clime
      clime about 11 years
      -n5 worked for my original command! And with -n100 it still works and is also fast. Thank you! If you could make that an answer...
    • Gilles 'SO- stop being evil'
      Gilles 'SO- stop being evil' about 11 years
      What is ll an alias for? does it include ls -L by any chance?
  • clime
    clime about 11 years
    You are right about that .[^.]* being redundant but there is not a symlink on the path to to that file. As I pointed out the file is being found by find and adding -n5 solved that problem. Also I didn't mention that but even files in the current directory were untouched. I am sure that the command was exiting prematurely. If I try to run the same command with the same data on another system, I get sudo: unable to execute /bin/chmod: Argument list too long Strangely I did not get this error on the system in question.
  • Stéphane Chazelas
    Stéphane Chazelas about 11 years
    @clime, yes, see how that error is reported by sudo and not by find. See the update to my answer.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 11 years
    xargs takes care of not passing a too long argument string. On the face of the question (which I'm betting is incomplete: there's probably a symlink involved somewhere) none of what you propose would make a difference.
  • alordiel
    alordiel about 11 years
    At least I was close. In the future, I will do more research before posting answers.