Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share|improve this question
    
Here's a little trick to quickly get data from any point in a series of pipes: find . 'type f | tee /dev/stderr | xargs command. That will write the output of the find command to stderr and stdout. stderr goes straight to the console and stdout is piped to xargs. –  Evan Teitelman Apr 17 '13 at 12:49
    
Try using find's -exec option: find . .[^.]* -type f -exec sudo chmod 664 -- '{}' \;. –  Evan Teitelman Apr 17 '13 at 12:55
    
You might be passing too many arguments to chmod. Try xargs -n5 to only allow 5 arguments per command. –  Evan Teitelman Apr 17 '13 at 12:58
    
the variant with -exec works, and the command execution is much longer. It seems that my original command crashes somewhere in the process. –  clime Apr 17 '13 at 12:58
    
-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... –  clime Apr 17 '13 at 13:06
show 1 more comment

3 Answers

up vote 3 down vote accepted

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.

share|improve this answer
    
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. –  clime Apr 17 '13 at 22:49
    
@clime, yes, see how that error is reported by sudo and not by find. See the update to my answer. –  Stephane Chazelas Apr 17 '13 at 22:58
    
Thank your for your in-depth explanation! –  clime Apr 17 '13 at 23:16
add comment

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|improve this answer
1  
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. –  Gilles Apr 17 '13 at 23:45
    
At least I was close. In the future, I will do more research before posting answers. –  Evan Teitelman Apr 18 '13 at 0:28
add comment

I don't see what you are trying to do with the directory name .[^.]* (I think it's redundant) and I don't think you need -print0 -0 options, I suspect you are getting a buffer overflow somewhere pipe or command line size

try

find . -f file  -xargs chmod {}\;

You could use -v on chmod to see ech file being changed.

share|improve this answer
    
.[^.]* includes hidden files and directories but not .. directory. -print0 and -0 options are useful if you have got filenames with spaces. It was not buffer overflow. It was just some limit on how many parameters chmod accepts (the rest is probably trashed). –  clime Apr 17 '13 at 13:47
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.