Before I recursively tar a directory i want to be able to delete all of the executables. In windows I would have simply deleted all of the files with .exe extensions. And I cannot simply delete all files with the executable mode since that would delete shell scripts as well. So is there any way to delete only non-shell script script files in a directory automatically?
-
3It might help us answer if you provided more context describing why you need to do this. – Zoredache Jul 01 '09 at 06:11
-
1Why don't you want to delete shell scripts? What about perl / python scripts? Do you only want to delete 'compiled' files? What about .pyc files (compiled python files)? In short this sounds like a very difficuly spec and I can't see why you'd want to do this. – Amandasaurus Jul 28 '09 at 08:34
-
You haven't specified what you want to happen if a file with executable mode is neither a shell script nor in an executable format. The simplest solution would certainly be to delete those incorrectly formatted files as well. But it might not be desirable if forgetting the `#!` line of a shell script would cause it to be deleted. – kasperd Sep 26 '15 at 15:47
8 Answers
I assume you mean binaries? If the number of executables is very large, this command may fail due to the command line for "file" and "echo" being too long. A quick example:
find /bin -type f | xargs file | grep "ELF.*executable" | awk -F: '{print $1}' | xargs echo
If you replace "echo" with "rm" in the above example, those files will be deleted. The "grep" command should prevent libraries from being deleted (you can test on /lib).
(Note, I only have access to FreeBSD right now, so the output of linux's "file" command may differ, thus changing what you need to do.)
This should give you an easy template for how to do it. You'll just need to be sure to know what to look for in the output given by "file" and grep accordingly.
Be careful. You could totally bork your system if you run this as root on the wrong directory.
![](../../users/profiles/10034.webp)
- 1,717
- 9
- 11
-
If the total length of the filenames is over the limit, `xargs` will do multiple commands as necessary; that's actually it's reason for existing in the first place. – dave_thompson_085 Sep 26 '15 at 17:15
find . -perm /111 -type f -exec echo rm -v {} \;
The magic here is that the -perm flag (for permissions) can take a / preceding the permission argument, which causes it to search for a logical OR on each of the bits. From the man page:
-perm /mode
Any of the permission bits mode are set for the file. Symbolic
modes are accepted in this form. You must specify 'u', 'g' or
'o' if you use a symbolic mode. See the EXAMPLES section for
some illustrative examples. If no permission bits in mode are
set, this test currently matches no files. However, it will
soon be changed to match any file (the idea is to be more con-
sistent with the behaviour of perm -000).
In case that wasn't clear, / specifies 111 specifies x in the user OR x in the group OR x in the others. And those are not XOR, so we're looking for at least one but up to 3.
Since the unix file permissions are
rwxrwxrwx
421421421
And we care about the x bits, we get a mask of
--1--1--1
or 111
It should be noted that in the command listed above, there's an echo to prevent you from shooting yourself in the foot. Feel free to take of the safety once you'd nailed the files correctly.
EDIT
OK, this blows away all shell scripts, too. There is no nice, neat way to do it with find that I've found, due to the relatively primitive regular expression matching. I can find all of the .sh files you want using regular expressions:
-regex ".*\(.*sh\$\)"
But I can't invert that at all. So I give up. I'm still leaving this up here, in case it's useful to someone.
Write a shell script, or use someone else's suggestion, or if you really want to, just temporarily remove executable permissions to the shell scripts, remove all the executable files, then add the +x back. Good luck.
![](../../users/profiles/4392.webp)
- 20,218
- 10
- 67
- 114
-
2The OP stated he didn't wish to nuke shell scripts, which your command would do. – Geoff Fritz Jul 01 '09 at 03:03
-
-
by one sec, I mean holy christ there's no way in hell using find. In order to negate a regular expression, you need to use a tool that has negative lookahead assertion, which gnu find doesn't do. – Matt Simmons Jul 01 '09 at 03:39
-
You can negate any test in `find` by preceding it with `!` as a separate arg; in most shells usually `!` invokes history expansion so to pass it to `find` you need to backslash or single-quote. `find` matches are always complete and don't need anchors, and here you don't need regex glob is fine: `'!' -name '*.sh'` But shell scripts aren't always named `something.sh`. – dave_thompson_085 Sep 26 '15 at 17:15
file * | grep executable | grep -v 'shell script' | cut -d: -f 1 | xargs rm
Or perhaps:
make clean
;-)
![](../../users/profiles/11202.webp)
- 103
- 12
Before I recursively tar a directory i want to be able to delete all of the executables.
IMHO the short answer is that there is no 100% foolproof way to do this. Depending on permissions alone may miss or include things you don't want. Depending on filenames won't work. Even depending on the output of the file command probably isn't a good idea, I have seen it miss-identify things on several occasions.
![](../../users/profiles/984.webp)
- 128,755
- 40
- 271
- 413
You need find
to iterate over the files in a tree, the file
command-line utility to determine their file type, followed by perl
to filter the executables:
find . -type f -print0 | xargs -0 file -N0 -- \
| perl -nlwe '/(.*\0).*ELF.*executable/ and printf $1' | xargs -0 ls -l --
You really want to delete the files replace the final ls -l --
with rm --
.
Note: all the -print0
and -0
switches are needed to make sure that spaces in the filenames are handled correctly. This is also the reason why perl
is used instead of awk
, which cannot really swallow null characters in the input. The --
switches are meant to protect against filenames starting a dash.
![](../../users/profiles/11295.webp)
- 111
- 1
You probably have to write a shell script to do this. Off the top of my head, run file first, get the executables and exclude all the scripts. Then you can operate on the remainder which should be what you are looking for.
![](../../users/profiles/97.webp)
- 5,337
- 2
- 18
- 17
Using bits from Matt's answer I came up with this:
find . -perm /111 -type f -print0 | xargs -0 file | grep -v 'shell script' | cut -d: -f1 | xargs echo rm
![](../../users/profiles/16407.webp)
- 141
- 2
I created a bash script to do just this. If you study all the lines in it I'm sure you can figure out how to do everything from the command line and modify it to delete any type of file, i.e. use "find -iname" or whatever.
#!/bin/bash
#this script will grab all executable files in the
#bash shell scripts ending in .sh will be saved
#current directory and delete them
#it will also destroy itself so keep a copy somewhere
echo "WARNING! This script will self-destruct and take all"
echo "executable files in the current directory when executed"
echo ""
#first create a new file to set up executable files
touch xdelgo.txt
#save all the bash shell scripts ending in .sh
#this will only work and in the pwd and not child directories
touch protected.sh
echo "#!/bin/bash" >> protected.sh
find . -type f | grep .sh | grep -v xdel.sh >> protect.txt
SAVE=$'\n';x=sudo;for l in $(<protect.txt);do echo -e "$x chmod u-x $l";
((x+1));done >> protected.sh
sudo chmod u+x protected.sh
./protected.sh
#throw all executable files from the current directory inside
find -type f -executable >> xdelgo.txt
echo "The following files have been marked for deletion"
echo " "
cat xdelgo.txt
echo " "
#create a new script that will execute deletion
touch xdelgo.sh
echo "#!/bin/bash" >> xdelgo.sh
echo "rm xdelgo.sh" >> xdelgo.sh
echo "rm xdelgo.txt" >> xdelgo.sh
echo "rm protect.txt" >> xdelgo.sh
#append rm to each line and throw into new executable file
DEL=$'\n';x=rm;for l in $(<xdelgo.txt);do echo -e "$x\t$l";((x+1));done
>> xdelgo.sh
#go give permission to the file
sudo chmod u+x xdelgo.sh
echo "bash scripts ending in .sh have been protected"
echo "run ./restore.sh to re-enable .sh files"
echo ""
touch restore.sh
echo "#!/bin/bash" >> restore.sh
find . | grep .sh | grep -v xdel.sh >> restore.txt
SAVE=$'\n';x=sudo;for l in $(<restore.txt);do echo -e "$x chmod u+x $l";
((x+1));done >> restore.sh
sudo chmod u+x restore.sh
echo "rm restore.txt" >> restore.sh
echo "rm restore.sh" >> restore.sh
echo "rm protected.sh" >> restore.sh
#THE POINT OF NO RETURN
echo "Are you sure you really wanna delete these files?"
echo "Press ENTER to continue, ctrl+c to abort"
read input
./xdelgo.sh
![](../../users/profiles/313423.webp)
- 1
- 2
-
The question explicitly asks for a solution which does not delete shell scripts. Additionally you script will have major issues with special characters in filenames. And the temporary files are not really needed. – kasperd Sep 26 '15 at 15:34