Lets say I have several directories, which are similar but are slightly different at the end:
XYZ_e6586_e5984
XYZ_e3282_e5984
XYZ_e9823_e5984
Now, in case there are two or more directories whose name is identical except the number between e and _ , only the directories with the highest number should be kept. In this case, XYZ_e6586_e5984 and XYZ_e3282_e5984 should be removed.
How do I do that?
Simple find regex case here:
find /directory -mindepth 1 -maxdepth 1 -type d -regextype sed -regex "XYZ_e[0-9]\{4}\_e5984 -print0" | sort -nr | tail -n +2 | xargs -i -0 rm -rf "{}"
Yet this will only work on linux with GNU find. A more portable but less pretty version is
find /directory -mindepth 1 -maxdepth 1 -type d -regextype sed -regex "XYZ_e[0-9][0-9][0-9][0-9]_e5984" | sort -nr | tail -n +2 | xargs -i rm -rf "{}"
Explanation:
Use -mindepth 1 and -maxdepth 1 to search only direct children of /directory.
-type -d specifies only searching for directories.
Regexes are pretty self explanatory in that case.
-print0 helps to deal with special characters
sort -nr sorts the output numericaly from highest to lowest
tail -n +2 skips first line (ie the highest numbered folder to keep)
xargs -i rm -rf "{}" performs the actual deletion (-0 is necessary because of -print0).
Just make sure the sort reverse gets done right (replace xargs -i rm -rf "{}" with echo "xargs -i rm -rf \"{}\"" to show the actual commands that would get executed.
If not sorted right, try export LANG=C before executing the command.
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
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.
I need to come up with a Bash script that will remove any folders within a directory if they meet both of the following criteria:
Older than 7 days.
Have an xml file in them that does not contain a certain string.
I know that this command works for removing all folders in the directory that are older than n days:
find ./ -type d -mtime +7 -exec rm -rf {}\;
And this command removes all of the files named kittens.xml that don't have the string <claws>18</claws>:
find ./* -name "kittens.xml" -type f\! -exec grep -L "<claws>18</claws>" {} \;| xargs rm -fv
But how do I remove all the folders that are more than one week old and don't contain that string?
FYI, I have very little prior experience with Bash.
for dir in `find /WHERE/ARE/THOSE/DIRS -type d -mtime +7 2>/dev/null` ; do
fgrep '<claws>18</claws>' "${dir}/kittens.xml" &>/dev/null || rm -fv "${dir}"
done
It loops through the found directories, then checks via fgrep for the needed string in the file, and if it's not found (e.g. not in the file OR the file is missing), removes the dir.
Note: it might cause damage, so think before run it. I'd run it first like this so it shows what it would remove...
fgrep '<claws>18</claws>' "${dir}/kittens.xml" &>/dev/null || echo "rm -fv ${dir}"
Also if there are subdirs, it might cause problems.
Try using the grep v or --invert-match tags:
find ./* -name "kittens.xml" -type f\! -exec grep -L -v "<claws>18</claws>" {} \;| xargs rm -fv
You can find more about grep invert by using this in terminal:
grep --help | grep invert
How can I move all files except one? I am looking for something like:
'mv ~/Linux/Old/!Tux.png ~/Linux/New/'
where I move old stuff to new stuff -folder except Tux.png. !-sign represents a negation. Is there some tool for the job?
If you use bash and have the extglob shell option set (which is usually the case):
mv ~/Linux/Old/!(Tux.png) ~/Linux/New/
Put the following to your .bashrc
shopt -s extglob
It extends regexes.
You can then move all files except one by
mv !(fileOne) ~/path/newFolder
Exceptions in relation to other commands
Note that, in copying directories, the forward-flash cannot be used in the name as noticed in the thread Why extglob except breaking except condition?:
cp -r !(Backups.backupdb) /home/masi/Documents/
so Backups.backupdb/ is wrong here before the negation and I would not use it neither in moving directories because of the risk of using wrongly then globs with other commands and possible other exceptions.
I would go with the traditional find & xargs way:
find ~/Linux/Old -maxdepth 1 -mindepth 1 -not -name Tux.png -print0 |
xargs -0 mv -t ~/Linux/New
-maxdepth 1 makes it not search recursively. If you only care about files, you can say -type f. -mindepth 1 makes it not include the ~/Linux/Old path itself into the result. Works with any filenames, including with those that contain embedded newlines.
One comment notes that the mv -t option is a probably GNU extension. For systems that don't have it
find ~/Linux/Old -maxdepth 1 -mindepth 1 -not -name Tux.png \
-exec mv '{}' ~/Linux/New \;
A quick way would be to modify the tux filename so that your move command will not match.
For example:
mv Tux.png .Tux.png
mv * ~/somefolder
mv .Tux.png Tux.png
I think the easiest way to do is with backticks
mv `ls -1 ~/Linux/Old/ | grep -v Tux.png` ~/Linux/New/
Edit:
Use backslash with ls instead to prevent using it with alias, i.e. mostly ls is aliased as ls --color.
mv `\ls -1 ~/Linux/Old/ | grep -v Tux.png` ~/Linux/New/
Thanks #Arnold Roa
For bash, sth answer is correct. Here is the zsh (my shell of choice) syntax:
mv ~/Linux/Old/^Tux.png ~/Linux/New/
Requires EXTENDED_GLOB shell option to be set.
I find this to be a bit safer and easier to rely on for simple moves that exclude certain files or directories.
ls -1 | grep -v ^$EXCLUDE | xargs -I{} mv {} $TARGET
This could be simpler and easy to remember and it works for me.
mv $(ls ~/folder | grep -v ~/folder/exclude.png) ~/destination
The following is not a 100% guaranteed method, and should not at all be attempted for scripting. But some times it is good enough for quick interactive shell usage. A file file glob like
[abc]*
(which will match all files with names starting with a, b or c) can be negated by inserting a "^" character first, i.e.
[^abc]*
I sometimes use this for not matching the "lost+found" directory, like for instance:
mv /mnt/usbdisk/[^l]* /home/user/stuff/.
Of course if there are other files starting with l I have to process those afterwards.
How about:
mv $(echo * | sed s:Tux.png::g) ~/Linux/New/
You have to be in the folder though.
This can bei done without grep like this:
ls ~/Linux/Old/ -QI Tux.png | xargs -I{} mv ~/Linux/Old/{} ~/Linux/New/
Note: -I is a captial i and makes the ls command ignore the Tux.png file, which is listed afterwards.
The output of ls is then piped into mv via xargs, which allows to use the output of ls as source argument for mv.
ls -Q just quotes the filenames listed by ls.
mv `find Linux/Old '!' -type d | fgrep -v Tux.png` Linux/New
The find command lists all regular files and the fgrep command filters out any Tux.png. The backticks tell mv to move the resulting file list.
ls ~/Linux/Old/ | grep -v Tux.png | xargs -i {} mv ~/Linux/New/'
move all files(not include except file) to except_file
find -maxdepth 1 -mindepth 1 -not -name except_file -print0 |xargs -0 mv -t ./except_file
for example(cache is current except file)
find -maxdepth 1 -mindepth 1 -not -name cache -print0 |xargs -0 mv -t ./cache