- Delete a list of files with find and grep
- 12 Answers 12
- Use rm to Delete Files and Directories on Linux
- The Basics of Using rm to Delete a File
- Options Available for rm
- -i Interactive mode
- -f Force
- -v Verbose
- -d Directory
- -r Recursive
- Combine Options
- -rf Remove Files and Directories, Even if Not Empty
- Combine rm with Other Commands
- Remove Old Files Using find and rm
- find’s «-exec rm <> \;» vs «-delete»
- 4 Answers 4
- Delete files with string found in file — Linux cli
- 8 Answers 8
Delete a list of files with find and grep
I want to delete all files which have names containing a specific word, e.g. «car». So far, I came up with this:
12 Answers 12
or pass the output of your pipeline to xargs :
find | grep car | xargs rm -f
Note that these are very blunt tools, and you are likely to remove files that you did not intend to remove. Also, no effort is made here to deal with files that contain characters such as whitespace (including newlines) or leading dashes. Be warned.
+1. But on the first few runs: change «rm» to «echo rm» . and verify that it only outputs file you really want to delete, and no others ^^
@nhahtdh: xargs: illegal option — d ; all the world is not LInux. And what about directories that contain \n in their name? If you worry about whitespace, you should use find —print0 . xargs -0
Please be aware that this will not work if your files contain unusual characters (like spaces) or start with a dash. Please see my answer for a fix.
To view what you are going to delete first, since rm -fr is such a dangerous command:
find /path/to/file/ | grep car | xargs ls -lh
Then if the results are what you want, run the real command by removing the ls -lh , replacing it with rm -fr
find /path/to/file/ | grep car | xargs rm -fr
It does exactly what you ask, logically running rm -rf on the what grep car returns from the output of find . which is a list of every file and folder recursively.
You really want to use find with -print0 and rm with — :
find [dir] [options] -print0 | grep --null-data [pattern] | xargs -0 rm --
A concrete example (removing all files below the current directory containing car in their filename):
find . -print0 | grep --null-data car | xargs -0 rm --
- -print0 , —null-data and -0 change the handling of the input/output from parsed as tokens separated by whitespace to parsed as tokens separated by the \0 -character. This allows the handling of unusual filenames (see man find for details)
- rm — makes sure to actually remove files starting with a — instead of treating them as parameters to rm . In case there is a file called -rf and do find . -print0 | grep —null-data r | xargs -0 rm , the file -rf will possibly not be removed, but alter the behaviour of rm on the other files.
Use rm to Delete Files and Directories on Linux
Estamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.
This guide shows how to use rm to remove files, directories, and other content from the command line in Linux.
To avoid creating examples that might remove important files, this Quick Answer uses variations of filename.txt . Adjust each command as needed.
The Basics of Using rm to Delete a File
rm filename1.txt filename2.txt
Options Available for rm
-i Interactive mode
Confirm each file before delete:
-f Force
-v Verbose
Show report of each file removed:
-d Directory
Note: This option only works if the directory is empty. To remove non-empty directories and the files within them, use the r flag.
-r Recursive
Remove a directory and any contents within it:
Combine Options
Options can be combined. For example, to remove all .png files with a prompt before each deletion and a report following each:
remove filename01.png? y filename01.png remove filename02.png? y filename02.png remove filename03.png? y filename03.png remove filename04.png? y filename04.png remove filename05.png? y filename05.png
-rf Remove Files and Directories, Even if Not Empty
Add the f flag to a recursive rm command to skip all confirmation prompts:
Combine rm with Other Commands
Remove Old Files Using find and rm
Combine the find command’s -exec option with rm to find and remove all files older than 28 days old. The files that match are printed on the screen ( -print ):
find filename* -type f -mtime +28 -exec rm '<>' ';' -print
In this command’s syntax, <> is replaced by the find command with all files that it finds, and ; tells find that the command sequence invoked with the -exec option has ended. In particular, -print is an option for find , not the executed rm . <> and ; are both surrounded with single quote marks to protect them from interpretation by the shell.
This page was originally published on Tuesday, July 3, 2018.
find’s «-exec rm <> \;» vs «-delete»
I noticed that the -exec . <> method is preferred. Why? Which one is safer/faster/better? I’ve used both on my Macbook and everything appears to work well.
4 Answers 4
-delete will perform better because it doesn’t have to spawn an external process for each and every matched file, but make sure to call this option after the filtering criteria (i.e. -name , -cmin , -type , etc.), otherwise it will delete the specified entire file tree. Indeed, quoting the find manpage :
GNU find searches the directory tree rooted at each given starting-point by evaluating the given expression from left to right, according to the rules of precedence (see section OPERATORS), until the outcome is known
find . -name .DS_Store -type f -delete
It is possible that you may see -exec rm <> + often recommended because -delete does not exist in all versions of find . I can’t check right now but I’m pretty sure I’ve used a find without it.
Both methods should be «safe».
A common method for avoiding the overhead of spawning an external process for each matched file is:
find / -name .DS_Store -print0 | xargs -0 rm
(but note that there is a portability problem here too: not all versions of find have -print0 !)
I see. I’ve also read that using the -delete switch before -name deletes the specified file tree, so I guess I have to be careful.
.DS_Store doesn’t contain any special characters at all, so the quotes are unnecessary and change nothing in this case.
Basically only whitespace (spaces, tabs, maybe others) is the only thing that causes an unquoted string to be interpreted as two separate command line arguments, but that’s not all you have to sorry about when deciding whether or not to quote. You have to worry about all shell metacharacters like ; or | or > or < and `` and many others which have special meaning to the shell unless quoted.
@MarcoMarsala xargs takes care of the limited-size argument list problem transparently by breaking it up into multiple invocations of the command.
Assuming .DS_Store represent files and not directories, the most portable still fast way to do it would be:
sudo find / -name .DS_Store -exec rm <> +
The only risk is for sudo not to be available but it is quite low nowadays.
The -delete option used to demand FreeBSD or later GNU find and is still non standard in a few other find implementations, so is not always available.
The command termination + instead of \; highly optimizes the exec clause by not running the rm command for each and every .DS_Store present on the file system. It is more ubiquitous, being specified by POSIX.
-delete is from FreeBSD from 1996, not added to GNU find until 2004, -exec cmd <> + is from SysV in 80s, standardised by POSIX in 1992, added to GNU find in 2005.
For a machine such as your macbook you won’t find much difference in performance between the two commands. However, if you look at the -exec version you can see a subtle difference:
sudo find / -iname ".file-to-delete" -exec rm <> \;
This means that you will find all those files with name .file-to-delete . However this search might return some unwanted false positives. When doing something with sudo you should be a bit more careful. The advantage of using -exec rm <> is that you can pass arguments to rm like this:
sudo find / -iname "*~" -exec rm -i <> \;
In this example I want to remove those backup files that emacs makes. However that tilde could be in some obscure file that I don’t know about and could be important. Plus I want to confirm the delete. So I put the option -i on the rm command. This will give me an interactive delete.
Also you can refine the usage of rm to delete directories as well as files:
find /usr/local/share/ -iname "useless" -exec rm -r <> \;
In brief, the -exec gives you a bit more control over the actual command that removes the found item. The advantage is that you use one tool to find the files, another tool to remove them. Also not every version of the find utility has the -delete option. So better to use each tool for its proper job. This is the unix philosophy — one tool, one job, use them together to do what you need to do.
Delete files with string found in file — Linux cli
I am trying to delete erroneous emails based on finding the email address in the file via Linux CLI. I can get the files with find . | xargs grep -l email@example.com But I cannot figure out how to delete them from there as the following code doesn’t work. rm -f | xargs find . | xargs grep -l email@example.com
8 Answers 8
Solution for your command:
grep -l email@example.com * | xargs rm
for file in $(grep -l email@example.com *); do rm -i $file; # ^ prompt for delete done
For several files, you can prevent the wildcard adding too many arguments by using grep -l -R —include=»*» email@domain.com ./ instead
sudo grep -lr ‘/directory/youd/like/to/delete/from/’ -e ‘text you would like to search’ | xargs rm This is what I used. I believe 2grit referenced the ‘-r’ for recursive, which was helpful in my case.
For safety I normally pipe the output from find to something like awk and create a batch file with each line being «rm filename»
That way you can check it before actually running it and manually fix any odd edge cases that are difficult to do with a regex
find . | xargs grep -l email@example.com | awk '' > doit.sh vi doit.sh // check for murphy and his law source doit.sh
I liked your approach, but for me couldn’t do it because I needed a cron job 😛 So I’m going with this one stackoverflow.com/a/4529188/656094
You can use find ‘s -exec and -delete , it will only delete the file if the grep command succeeds. Using grep -q so it wouldn’t print anything, you can replace the -q with -l to see which files had the string in them.
find . -exec grep -q 'email@example.com' '<>' \; -delete
yeah, but nothing as expected. find . |grep ‘t-bone@spechal.com’ at other hand works just fine. I’m on a mac, btw. my answer there solved it for me anyway. 😉
I liked Martin Beckett’s solution but found that file names with spaces could trip it up (like who uses spaces in file names, pfft :D). Also I wanted to review what was matched so I move the matched files to a local folder instead of just deleting them with the ‘rm’ command:
# Make a folder in the current directory to put the matched files $ mkdir -p './matched-files' # Create a script to move files that match the grep # NOTE: Remove "-name '*.txt'" to allow all file extensions to be searched. # NOTE: Edit the grep argument 'something' to what you want to search for. $ find . -name '*.txt' -print0 | xargs -0 grep -al 'something' | awk -F '\n' '< print "mv \""$0"\" ./matched-files" >' > doit.sh Or because its possible (in Linux, idk about other OS's) to have newlines in a file name you can use this longer, untested if works better (who puts newlines in filenames? pfft :D), version: $ find . -name '*.txt' -print0 | xargs -0 grep -alZ 'something' | awk -F '\0' '< for (x=1; x' > doit.sh # Evaluate the file following the 'source' command as a list of commands executed in the current context: $ source doit.sh
NOTE: I had issues where grep could not match inside files that had utf-16 encoding. See here for a workaround. In case that website disappears what you do is use grep’s -a flag which makes grep treat files as text and use a regex pattern that matches any first-byte in each extended character. For example to match Entité do this:
and if that doesn’t work then try this: