Removing files with rm using find and xargs - linux

When I do
rm file.txt
or
rm *.txt
I'm prompted for each file, since I didn't specify the -f option to rm.
But when I do this:
find . -type f -name '*.txt' | xargs rm
the files are removed without the confirmation.
What is the logics behind this? Is it possible to find the reason in some documentation? I cannot explain why this would be the case.

You have an alias set for the rm command to 'rm -i'. Therefore if you invoke the command directly as in
rm file.txt
or
rm *.txt
the alias will be expanded. If you will call it with xargs as in
find . -type f -name '*.txt' | xargs rm
The rm is passed as a simple string argument to xargs and is later invoked by xargs without alias substitution of the shell.
You alias is probably defined in ~/.bashrc, in case you want to remove it.

you can use this simple command to solve your problem
find . -type f -name '*.txt' -delete

Depending on your version of xargs you may have the --no-run-if-empty GNU extension option available to you:
find . -type f -name '*.txt' | xargs --no-run-if-empty rm -rf

Related

Remove all files contain specific string - Bash

I have these bad data
AWS-Console.pngcrop-AWS-Console.png
Alimofire.pngcrop-Alimofire.png
Amazon-ECR-.pngcrop-Amazon-ECR-.png
Amazon-ECS.pngcrop-Amazon-ECS.png
Amazon-RDS.pngcrop-Amazon-RDS.png
Angular.pngcrop-Angular.png
AngularJS.pngcrop-AngularJS.png
.... 1000 more
I'm trying to delete them
I've tried
ls public/assets/fe/img/skill/ | grep crop | rm -rf *crop*
ls public/assets/fe/img/skill/ | grep crop | rm -rf
rm -rf $(ls public/assets/fe/img/skill/ | grep crop)
None of them work ...
rm can handle the glob expressions that ls handles:
rm public/assets/fe/img/skill/*crop*
Use the find command instead
find . -name "*crop*" -type f -exec rm -i {} \;
-type f will specify to search file only and avoid directories
-exec requires the command input to end with \;, the {} being substitute by the result of the command
the -i will ask you to confirm ; remove it once sure what you do.
advice display the result beforehand with -print in place of -exec ...
find . -name "*crop*" -type f -print
More here where your question would find more accurate answers
The main problem in your commands is the missing path in the output of the ls command.
ls public/assets/fe/img/skill/ | grep crop will retur e.g. AWS-Console.pngcrop-AWS-Console.png which is passed to rm. But rm AWS-Console.pngcrop-AWS-Console.png fails because there is no such file in the current directory. It should be rm public/assets/fe/img/skill/AWS-Console.pngcrop-AWS-Console.png instead.
Adding -d to the ls command should do the trick:
ls -d public/assets/fe/img/skill/ | grep crop | rm -rf
rm -rf $(ls -d public/assets/fe/img/skill/ | grep crop)
As pointed out in other answers, other solutions exist, including:
rm public/assets/fe/img/skill/*crop*
find public/assets/fe/img/skill/ -name "*crop*" -type f -exec rm -i {} \;
If it's a really large number of files (apparently wasn't in your case), xargs can speed up the process. This applies for a lot of things you might want to read from a pipe.
find . -name "*crop*" -type f | xargs rm
The main advantage of using find here is that it's an easy way to ignore directories. If that's not an issue, let the OS handle all that.
printf "%s\n" public/assets/fe/img/skill/*crop* | xargs rm
If you need to be able to pick up files in subdirectories -
shopt -s globstar # double asterisks not include arbitrary preceding paths
printf "%s\n" public/assets/fe/img/skill/**crop* | xargs rm
You might want to look over the list first, though.
printf "%s\n" public/assets/fe/img/skill/*crop* >crop.lst
# check the list - vi, grep, whatever satisfies you.
xargs rm < crop.lst # fast-delete them in bulk

How can I move many files without having Argument list too long?

I am trying to move about 700,000 .jpg files from one directory to another in my Ubuntu server. I tried the following:
xargs mv * -t /var/www/html/
and
echo (*.jpg|*.png|*.bmp) | xargs mv -t /var/www/html/
and
echo (*.jpg) | xargs mv -t /var/www/html/
and
find . -name "*.jpg" -print0 | xargs mv * ../
and they all give me the same error: /usr/bin/xargs: Argument list too long
what should I do? Please help me out. Thanks :)
If you use find I would recommend you to use the -exec attribute. So your result should be find . -name "*.jpg" -exec mv {} /home/new/location \;.
However I would recommend to check what the find command returns you, replacing the exec part with: -exec ls -lrt {} \;
Try:
find /path/to/old-directory -type f | xargs -i mv "{}" /path/to/new-directory
You could have tried:
for f in *.jpg do;
mv -tv $f /var/www/html/
done
for f in *.png do;
mv -tv $f /var/www/html/
done
for f in *.bmp do;
mv -tv $f /var/www/html/
done
also, you should carefully read xargs(1); I strongly suspect that
find . -name "*.jpg" -print0 | xargs -n 1000 -I '{}' mv '{}' ../
should work for you
At last, learn more about rename(1). It is probably enough for the job.

How to pipe the results of 'find' to mv in Linux

How do I pipe the results of a 'find' (in Linux) to be moved to a different directory? This is what I have so far.
find ./ -name '*article*' | mv ../backup
but its not yet right (I get an error missing file argument, because I didn't specify a file, because I was trying to get it from the pipe)
find ./ -name '*article*' -exec mv {} ../backup \;
OR
find ./ -name '*article*' | xargs -I '{}' mv {} ../backup
xargs is commonly used for this, and mv on Linux has a -t option to facilitate that.
find ./ -name '*article*' | xargs mv -t ../backup
If your find supports -exec ... \+ you could equivalently do
find ./ -name '*article*' -exec mv -t ../backup {} \+
The -t option is a GNU extension, so it is not portable to systems which do not have GNU coreutils (though every proper Linux I have seen has that, with the possible exception of Busybox). For complete POSIX portability, it's of course possible to roll your own replacement, maybe something like
find ./ -name '*article*' -exec sh -c 'mv "$#" "$0"' ../backup {} \+
where we shamelessly abuse the convenient fact that the first argument after sh -c 'commands' ends up as the "script name" parameter in $0 so that we don't even need to shift it.
Probably see also https://mywiki.wooledge.org/BashFAQ/020
I found this really useful having thousands of files in one folder:
ls -U | head -10000 | egrep '\.png$' | xargs -I '{}' mv {} ./png
To move all pngs in first 10000 files to subfolder png
mv $(find . -name '*article*') ../backup
Here are a few solutions.
find . -type f -newermt "2019-01-01" ! -newermt "2019-05-01" \
-exec mv {} path \;**
or
find path -type f -newermt "2019-01-01" ! -newermt "2019-05-01" \
-exec mv {} path \;
or
find /Directory/filebox/ -type f -newermt "2019-01-01" \
! -newermt "2019-05-01" -exec mv {} ../filemove/ \;
The backslash + newline is just for legibility; you can equivalently use a single long line.
xargs is your buddy here (When you have multiple actions to take)!
And using it the way I have shown will give great control to you as well.
find ./ -name '*article*' | xargs -n1 sh -c "mv {} <path/to/target/directory>"
Explanation:
-n1
Number of lines to consider for each operation ahead
sh -c
The shell command to execute giving it the lines as per previous condition
"mv {} /target/path"
The move command will take two arguments-
1) The line(s) from operation 1, i.e. {}, value substitutes automatically
2) The target path for move command, as specified
Note: the "Double Quotes" are specified to allow any number of spaces or arguments for the shell command which receives arguments from xargs

Recursively remove files

Does anyone have a solution to remove those pesky ._ and .DS_Store files that one gets after moving files from a Mac to A Linux Server?
specify a start directory and let it go? like /var/www/html/ down...
change to the directory, and use:
find . -name ".DS_Store" -print0 | xargs -0 rm -rf
find . -name "._*" -print0 | xargs -0 rm -rf
Not tested, try them without the xargs first!
You could replace the period after find, with the directory, instead of changing to the directory first.
find /dir/here ...
find /var/www/html \( -name '.DS_Store' -or -name '._*' \) -delete
Newer findutils supports -delete, so:
find . -name ".DS_Store" -delete
Add -print to also get a list of deletions.
Command will work for you if you have an up-to-date POSIX system, I believe. At least it works for me on OS X 10.8 and works for others who've tested it on macOS 10.12 (Mojave).
Credit to #ephemient in a comment on #X-Istence's post (thought it was helpful enough to warrant its own answer).
Simple command:
rm `find ./ -name '.DS_Store'` -rf
rm `find ./ -name '._'` -rf
Good luck!
cd /var/www/html && find . -name '.DS_Store' -print0 | xargs -0 rm
cd /var/www/html && find . -name '._*' -print0 | xargs -0 rm
You could switch to zsh instead of bash. This lets you use ** to match files anywhere in a directory tree:
$ rm /var/www/html/**/_* /var/www/html/**/.DS_Store
You can also combine them like this:
$ rm /var/www/html/**/(_*|.DS_Store)
Zsh has lots of other features that bash lacks, but that one alone is worth making the switch for. It is available in most (probably all) linux distros, as well as cygwin and OS X.
You can find more information on the zsh site.
find . -name "FILE-TO-FIND"-exec rm -rf {} \;
Example to delete "Thumbs.db" recursively;
find . -iname "Thumbs.db" -print0 | xargs -0 rm -rf
Validate by:
find . -iname "Thumbs.db"
This should now, not display any of the entries with "Thumbs.db", inside the current path.
It is better to see what is removing by adding -print to this answer
find /var/www/html \( -name '.DS_Store' -or -name '._*' \) -delete -print
if you have Bash 4.0++
#!/bin/bash
shopt -s globstar
for file in /var/www/html/**/.DS_Store /var/www/html/**/._
do
echo rm "$file"
done
A few things to note:
'-delete' is not recursive. So if .TemporaryItems (folder) has files in it, the command fails.
There are a lot of these pesky files created by macs:
.DS_Store
._.DS_Store
.TemporaryItems
.apdisk
This one command addresses all of them. Saves from running find over and over again for multiple matches.
find /home/foo \( -name '.DS_Store' -or -name '._.DS_Store' -or -name '._*' -or -name '.TemporaryItems' -or -name '.apdisk' \) -exec rm -rf {} \;
This also works:
sudo rm -rf 2018-03-*
here your deleting files with names of the format 2018-03-(something else)
keep it simple

Linux command for removing all ~ files

What command can I use in Linux to check if there is a file in a given directory (or its subdirectories) that contains a ~at the end of the file's name?
For example, if I'm at a directory called t which contains many subdirectories, etc, I would like to remove all files that end with a ~.
Watch out for filenames with spaces in them!
find ./ -name "*~" -type f -print0 | xargs -0 rm
with GNU find
find /path -type f -name "*~" -exec rm {} +
or
find /path -type f -name "*~" -delete
find ./ -name '*~' -print0 | xargs -0 rm -f
Here find will search the directory ./ and all sub directories, filtering for filenames that match the glob '*~' and printing them (with proper quoting courtesy of alberge). The results are passed to xargs to be appended to rm -f and the resulting string run in a shell. You can use multiple paths, and there are many other filters available (just read man find).
you can use a find, grep, rm combination, something like
find | grep "~" | xargs rm -f
Probably others have better ideas :)

Resources