Well, to be specific, it was chmod -R 755. Now every file is executable, which I don't want. I am thinking that I should look at the first two bytes of each file for the #!, but will this cover everything? Should I instead use file to look at everything and base my decision on that? Or, more likely, is there an even better way to do this?

What is the preferred way to recursively go through a directory and set -x on files that are not 'supposed to be' executable?

  • Did you do this at / or another directory?
    – gvkv
    Aug 27, 2010 at 19:09
  • 1
    @gvkv: Not /, a directory completely owned by me.
    – Larry Wang
    Aug 27, 2010 at 19:12
  • 1
    @Larry in the future you should probably use some variant of +x rather than raping all permissions, and possibly causing writes on all files. Aug 27, 2010 at 20:38
  • @xenoterracide: Agreed. What I really wanted was to give group same permissions as me (which did end up happening!), I just didn't do enough thinking before I typed.
    – Larry Wang
    Aug 27, 2010 at 20:48
  • How many files are we talking about? How many should be executable? Is there any way to tell from the file names? Aug 27, 2010 at 21:48

3 Answers 3


There's no magic bullet here. The permissions carry information which is not always redundant.

If you'd done this in a system directory, your system would be in a very bad state, because you'd have to worry about setuid and setgid bits, and about files that are not supposed to be world-readable, and about files that are supposed to be group- or world-writable.

In a per-user directory, you have to worry about files that aren't supposed to be world-readable. No one can help you there.

As for executability, a good rule of thumb would be to make everything that doesn't look like it could be executed, be nonexecutable. The kernel can execute scripts whose first two bytes are #!, ELF binaries whose first four bytes are \x7fELF where \x7f is the byte with the value 12, and a few rarer file types (a.out, anything registered with binfmt_misc). Hence the following command should restore your permissions to a reasonable state (assumes bash 4 or zsh, otherwise use find to traverse the directory tree; warning, typed directly into the browser):

for x in **/*; do
  if ! [ -f "$x" ]; then continue; fi # skip all but regular files
  case $(head -c 4 "$x") in
    "#!"??) :;; # skip script
    "\x7fELF") :;; # skip ELF executable
    *) chmod a-x "$x";;

Note that there is a simple way to back up and restore permissions of a directory tree, on Linux and possibly other unices with ACL support:

getfacl -R >saved-permissions
setfacl --restore=saved-permissions
  • Thanks! Fortunately, I think everything falls into these two categories. If this misses anything, I can deal with it later.
    – Larry Wang
    Aug 27, 2010 at 21:35
  • Bear in mind that **/* requires globstar.
    – Chris Down
    Nov 7, 2011 at 14:03
  • I'd recommend two changes to this script. One, use find rather than globstar; two, instead of looking at the head, use the file command to see what it is and branch from there. Nov 12, 2011 at 15:52
  • @Shadur find is less reliable than globstar, globstar is preferable in almost every case.
    – Chris Down
    Nov 15, 2011 at 21:58
  • 1
    @Gilles Preferable as in "if you have it, it's far superior", not only is it faster, it's also more reliable (and doesn't have unexpected SNAFUs).
    – Chris Down
    Nov 15, 2011 at 22:11

I believe you will want something like

find dir -type f -exec chmod ugo-x '{}' +

This looks for all regular files, recursively in dir, (it excludes directories, and devices) and removes the executable bit.

I would start here, and then work my way towards making files that are supposed to be executable, executable.

The following should work exactly as you asked for ( it will find all regular files, grep them for #! and then remove the x bits if not found )

find . -type f | xargs grep -L #! | xargs chmod ugo-x

possibly a better version of the above (less pipes)

find . -type f -exec grep -L #! '{}' + | xargs chmod ugo-x 
  • 3
    Make that grep -L '^#!' at least (the quotes are necessary, and ^ restricts to matching at the beginning of the line), but it's still too permissive as it matches #! on any line. Using xargs will fail with file names containing spaces or quoting characters; use xargs -d '\n' (requires GNU xargs). Aug 27, 2010 at 21:30

Well, without a shebang line, the file will be executed as a shell script, nominally with /bin/sh. You're idea is a good start and on the assumption that the directory in question doesn't contain mission-critical files there probably isn't much risk to executing some grep and chmod combo. You may encounter false positives, i.e., files with a shebang line that are not meant to have their executable bit set but without knowing more information on the purpose of what's in the directory, only you can decide whether that poses a significant existential threat to your system and/or data.

  • I'm not worried so much about false positives as false negatives. There are binaries that I don't think start with #!.
    – Larry Wang
    Aug 27, 2010 at 19:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .