4

I have a folder containing many C source file and the same number of its compiled output files.

I set executable bit to all this files but for some reason it was lost, making them not executable. (I think that's because I moved that folder to a NTFS partition for a while).

I would like to make them (but not the other files in my directory) executable again. One possible way is to move all source file from that folder to a temporary folder and do:

chmod +x *

Then move source files back to current folder.

Is it possible to do the same without moving files around?

Note:

Compiled output files are extension-less (in C).

4 Answers4

12

That depends on how you define which files should be set as executables. For example, if consider all the files that do not have dot in filename, you can use:

find -type f -not -name "*.*" -exec chmod +x \{\} \;

This will find recursively all the files (not directories) that do not have dot in file name and set them executable. If you want to limit this to only current directory, add -maxdepth 1 argument, like this:

find -maxdepth 1 -type f -not -name "*.*" -exec chmod +x \{\} \;

You could also rely on file command which could tell you if a file is ELF executable. To do this, you could run something like:

find -type f -exec /bin/sh -c "file {} | grep -q executable && chmod +x {}" \;

This will recursively find all regular files and call file command on them. Then, grep will look for "executable" string in the output of this command (it should be something like ELF 32-bit LSB executable ...) and only if it finds it, chmod will be called on this file.

Of course, you can also add -maxdepth 1 in this case to disable recursive searching.

4

You could do something like:

find /pathtostuff -type f ! -name "*.c" -exec chmod +x {} +

This looks in /pathtostuff for files that lack a .c extension and adds the execute permissions to them.

UPDATE

This can be expanded to handle other C extensions too:

find . -type f ! -name "*.c" ! -name "*.h" ! -name "*.cc" ! -name "*.cpp" -exec chmod +x {} +

ALTERNATIVE APPROACH

This script is extension agnostic. It looks for files that are "binary" as opposed to "text".

#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;
my @dir = @ARGV ? @ARGV : ('.');
my @files;
find( sub { -f $_ && !-T $_ && push @files, $File::Find::name }, @dir );
for my $file (@files) {
    system("echo  +x $file");
}

Simply pass the directory path to be examined. Run the script without any argument to process the current working directory.

JRFerguson
  • 14,740
  • I'm surprised! It didn't asked for password. – Santosh Kumar Oct 04 '12 at 15:42
  • What if I want to exclude another extension? Say .cc. – Santosh Kumar Oct 04 '12 at 15:42
  • 1
    This is not really ideal since there may be .o/.h/.cpp and other files. – Krzysztof Adamski Oct 04 '12 at 15:45
  • 1
    @SantoshKumar: it don't need password for anything. Password is only needed when you use sudo command which is used to gain root privileges. As long as you run chmod on the files owned by the user you are logged in as, you don't have to use sudo. – Krzysztof Adamski Oct 04 '12 at 16:10
  • @KrzysztofAdamski The find arguments can easily be amended to specify *.c, '*h and *.cc as necessary. My initial response was to lead the OP to one solution. – JRFerguson Oct 04 '12 at 16:33
  • @JRFerguson: I'm sure your answer is helpful for OP but my comment was here to let you know that you could enhance it by showing how to exclude more file extensions by using multiple ! -name XXX arguments. Also, my point is, that in some cases it may be hard to exclude all possible extensions, especially if you are writing a script and you don't know where it is going to be run. Because of this, it's better to have more general solution. – Krzysztof Adamski Oct 04 '12 at 16:40
  • @KrzysztofAdamski I enhanced my response to address other file extensions. Indeed, the issue is that one needs to cover all possibilities, but that is best examined for the specific data. I don't disagree with your comments. – JRFerguson Oct 04 '12 at 16:43
  • @JRFerguson: Now my only comment is that you don't have to group your tests with parenthesises as there is no problem with operators priority and you don't have to specify -a as AND is default operator. The same way you didn't have to use it when you only used -type and -name. – Krzysztof Adamski Oct 04 '12 at 16:56
  • @KrzysztofAdamski Point well-taken; noted and post updated. – JRFerguson Oct 04 '12 at 17:00
  • 1
    @JRFerguson: I don't want to be annoying with my comments but there may be some binary files that are not executable in the directory. For example .o files. Also consider how perl determines if file is binary or text file - it reads a block of file and check how many "odd" characters (controlcodes, metacharacters) are there. If it's more than 10% of such characters or if there is at least one null character in this block, it's considered binary file. This may produce some wrong results. In general, there is no good way to tell if it's binary or text file. – Krzysztof Adamski Oct 04 '12 at 19:45
  • @KrzysztofAdamski You are right, there are other files in that directory. There is one Makefile which is also extension-less, I'm a bit confused how do I exclude them. – Santosh Kumar Oct 05 '12 at 01:41
  • @SantoshKumar: I believe using file utility as suggested in my answer is the best you can get. Of course it can still produce some false positives but it's very unlikely. – Krzysztof Adamski Oct 05 '12 at 07:00
3

In zsh:

setopt extendedglob
chmod +x -- ^*.c

With ksh, or bash with extglob option, or zsh with kshglob option:

chmod +x -- !(*.c)

With ksh93 (probably best to avoid playing with FIGNORE though):

FIGNORE='@(.*|*.c)'
chmod +x -- *

For all, replace *.c with *.* to exclude anything that has a dot.

With zsh, you can do nice things with globbing qualifiers, like:

oftype() [[ $(file -b --mime-type -- $REPLY) = $type ]]
type=application/x-executable; chmod +x -- **/*(.+oftype)

Which would match on regular files "oftype" application/x-executable (recursively descending into subdirectories).

1

This would also likely work

chmod +x $(find . -type f -print0 | xargs -0 file | grep "ELF [^,;]* executable" |  cut -d: -f1)

You'll still have problems if your files have spaces, tabs, newlines, asterisks, question marks, angle bracket or backslash characters in the names, or if there are too many of them to fit in one command line.

doneal24
  • 5,059