What the best way to delete files by extension? - linux

I am looking the best way to delete files from directory by extension.
I am planning to do it by date. But now, i am testing how it works.
This:
dir=/tmp/backup/
mask="jpeg jpg png gif bmp pdf"
for i in $mask; do
find $dir -name "*.$i" -type f -delete
done
Or this ?
find $dir \( -name "*.jpeg" -o -name "*.jpg" -o -name "*.png" \
-o -name "*.gif" -o -name "*.bmp" -o -name "*.pdf" \) -type f -delete
I wan to do it with min resources of machine and operation system. Maybe you know other ways to do it. Because i will delete one year old files. And it can call lags. Thanks.

You can just use:
# to ensure it doesn't return *.jpg if there is no .jpg file
shopt -s nullglob
# list all matching extension filea
echo *.{jpeg,jpg,png,gif,bmp,pdf}
When you are satisfied with the output, just replace echo by rm
However if you want to make use of a variable then store all extensions in a variable then use it like this with find command:
mask="jpeg jpg png gif bmp pdf"
find . -type f -regextype posix-extended -regex ".*\.("${mask// /|}")"

Related

Bash script not moving files

I have a bash script that is designed to run in a Linux directory that contains only a collection of image files and video files of varying formats. Once executed, the script looks to see if the Vids and Pics subdirectories exist, and if not, creates them. Then all of the image files are supposed to get moved into Pics and the video files moved into Vids.
But when the script executes the directories get created but none of the files are moved into them.
Any bash experts out there that can take a quick look and suggest a fix?
#!/bin/bash
echo "This script will check for the existence of 'Vids' and 'Pics' subdirectories and create them if they do not exist. It will then move all image files into 'Pics' and all video files into 'Vids'. Do you wish to proceed? (y/n)"
read proceed
if [ $proceed == "y" ]; then
if [ ! -d "Vids" ]; then
mkdir Vids
fi
if [ ! -d "Pics" ]; then
mkdir Pics
fi
find . -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" -exec mv {} Pics/ \;
find . -name "*.mp4" -o -name "*.avi" -o -name "*.mkv" -o -name "*.wmv" -exec mv {} Vids/ \;
echo "Image files have been moved to 'Pics' and video files have been moved to 'Vids'."
else
echo "Exiting script."
fi
I named the script test.sh and gave it execute permission. When I ran the script, it was run in a directory with a large number of both image and video files. The script asked me if I wanted to continue. When I said yes, it said the directories Vids and Pics was created and all the files moved into them. The script then ends. But none of the files have been moved, although the directories Vids and Pics was created.
The implicit AND operator has higher precedence than -o, so your command is equivalent to:
find . -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o \( -name "*.gif" -exec mv {} Pics/ \; \)
so it only performs -exec for *.gif, not the other extensions. You need to put parentheses around all the -name expressions.
find . \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" \) -exec mv {} Pics/ \;

Find Command with multiple file extensions

I'm looking through many sub directories and finding all the files ending in .JPG .jpg and .png and copying them to a separate directory, however just now its only finding .JPG
Could someone explain what i'm doing wrong?
find /root/TEST/Images -name '*.png' -o -name '*.jpg' -o -name '*.JPG' -exec cp -t /root/TEST/CopiedImages {} +
You have to group the -o conditions because -a, the implied AND between the last -name '*.JPG' and -exec has higher precedence:
find /root/TEST/Images \( -name '*.png' -o -name '*.jpg' -o -name '*.JPG' \) -exec cp -t /root/TEST/CopiedImages {} +
Grouping is done with parentheses, but they have to be escaped (or quoted) due to their special meaning is shell.
Unrelated to this, you can shorten the overall expression by combining filters for jpg and JPG with the case-insensitive -iname (as noted in comments):
find /root/TEST/Images \( -name '*.png' -o -iname '*.jpg' \) -exec cp -t /root/TEST/CopiedImages {} +

Append find result to txt file

I am trying to concat jpg and png images with the use of ffmpeg. Before doing so, I am trying to append the file name and paths to a txt file. However, I am getting an error. The files have to follow the format file '/media/test.jpg' for appending to the txt file. What would be the best way to achieve this?
Print files:
find . \( -name '*.jpg' -o -name '*.png' \) -print
Error when trying this:
find . \( -name '*.jpg' -o -name '*.png' \) printf "file '%s'\n" > mylist.txt
find . \( -name '*.jpg' -o -name '*.png' \) -printf "file '%p'\n" >> mylist.txt

How to recursively delete multiple files with different extensions?

I am trying to write a command to remove recursively several files with different extensions (*.extension1, *.extension2 etc) from the current directory and all its related sub-directories.
So far I got this command from another post but I couldn't workout how to adapt it to more than one file in the same command line:
find . -name "*.extension1" -type f -delete
Is it as simple as below?
find . -name "*.extension1";"*.extension2" -type f -delete
Just as a side note, these are all output files that I do not need, but not all are necessarily always output so some of the extensions might not be present. This is just as a general clean-up command.
find . \( -name "*.extension1" -o -name "*.extension2" \) -type f -delete
find Documents ( -name ".py" -o -name ".html" ) -exec file {} \;
OR
find . -regextype posix-egrep -regex ".*\.(extension1|extension2)$" -type f -delete
Just add more options. A regex solution can also apply but this one's better and safer.
find . \( -name '*.ext1' -or -name '*.ext2' \) -type f -delete
Edit: You probably need -or as well. Before deleting, test it first without the -delete option. (2) Added a pair of () as suggested by gniourf_gniourf.
Maybe regexp will help you
find . -regextype posix-awk -regex "(.*.ext1|.*.ext2)" -type f -delete
Another solution using rm:
rm -rf ./**/*.{txt,nfo,jpg,jpeg,png,exe,url}
If you want to delete other files too for e.g. those starting with sample. just add that too:
rm -rf ./**/*.{txt,nfo,jpg,jpeg,png,exe,url} ./**/*/sample.*
This simple command will delete all the files with extension1 and extension2 recursively in that directory.
rm find . -name *.extension1 -o -name *.extentions2

Exclude list of files from find

If I have a list of filenames in a text file that I want to exclude when I run find, how can I do that? For example, I want to do something like:
find /dir -name "*.gz" -exclude_from skip_files
and get all the .gz files in /dir except for the files listed in skip_files. But find has no -exclude_from flag. How can I skip all the files in skip_files?
I don't think find has an option like this, you could build a command using printf and your exclude list:
find /dir -name "*.gz" $(printf "! -name %s " $(cat skip_files))
Which is the same as doing:
find /dir -name "*.gz" ! -name first_skip ! -name second_skip .... etc
Alternatively you can pipe from find into grep:
find /dir -name "*.gz" | grep -vFf skip_files
This is what i usually do to remove some files from the result (In this case i looked for all text files but wasn't interested in a bunch of valgrind memcheck reports we have here and there):
find . -type f -name '*.txt' ! -name '*mem*.txt'
It seems to be working.
I think you can try like
find /dir \( -name "*.gz" ! -name skip_file1 ! -name skip_file2 ...so on \)
find /var/www/test/ -type f \( -iname "*.*" ! -iname "*.php" ! -iname "*.jpg" ! -iname "*.png" \)
The above command gives list of all files excluding files with .php, .jpg ang .png extension. This command works for me in putty.
Josh Jolly's grep solution works, but has O(N**2) complexity, making it too slow for long lists. If the lists are sorted first (O(N*log(N)) complexity), you can use comm, which has O(N) complexity:
find /dir -name '*.gz' |sort >everything_sorted
sort skip_files >skip_files_sorted
comm -23 everything_sorted skip_files_sorted | xargs . . . etc
man your computer's comm for details.
This solution will go through all files (not exactly excluding from the find command), but will produce an output skipping files from a list of exclusions.
I found that useful while running a time-consuming command (file /dir -exec md5sum {} \;).
You can create a shell script to handle the skipping logic and run commands on the files found (make it executable with chmod, replace echo with other commands):
$ cat skip_file.sh
#!/bin/bash
found=$(grep "^$1$" files_to_skip.txt)
if [ -z "$found" ]; then
# run your command
echo $1
fi
Create a file with the list of files to skip named files_to_skip.txt (on the dir you are running from).
Then use find using it:
find /dir -name "*.gz" -exec ./skip_file.sh {} \;
This should work:
find * -name "*.gz" $(printf "! -path %s " $(<skip_files.txt))
Working out
Assuming skip_files has a filename on each line, you can get the list of filenames via $(<skip_files.txt). E.g. echo $(<skip_files.txt) should print them all out.
For each filename you want to have a ! -path filename expression. To build this, use $(printf "! -path %s " $(<skip_files.txt))
Then, put it together with a filter on -name "*.gz"

Resources