How do I write a command to search and remove files, even in cases where the files can't be found? - linux

I’m using Amazon Linux with the bash shell. I want to find and remove some PDF files in a single line, so I tried
find /home/jboss/.jenkins/jobs/myco/workspace/ebook/ -name '*.pdf' | xargs rm
This works fine if there are PDF files. But if there are none, I get the error
rm: missing operand
Is there any way to write the above statement in a single line so that it will not fail, even if there are no files to remove?

This can easily be achieved using the -r flag to xargs.
I also recommend using "special character tolerant" version:
find /home/jboss/.jenkins/jobs/myco/workspace/ebook/ -name '*.pdf' -print0 | xargs -0 -r rm

Have you tried doing it all within the find command?
find /home/jboss/.jenkins/jobs/myco/workspace/ebook/ -name '*.pdf' -exec rm -f {} \;
I've always used the construct above though I believe you can also use the switch -delete which may be a bit more efficient. If you do use it remember to put -delete at the end as find is evaluated left to right as an expression.

You don't even need find, you can simply use rm, it supports basic pattern matching. Just do the following:
rm -f path/*.pdf

Related

renaming with find

I managed to find several files with the find command.
the files are of the type file_sakfksanf.txt, file_afsjnanfs.pdf, file_afsnjnjans.cpp,
now I want to rename them with the rename and -exec command to
mywish_sakfksanf.txt, mywish_afsjnanfs.pdf, mywish_afsnjnjans.cpp
that only the first prefix is changed. I am trying for some time, so don't blame me for being stupid.
If you read through the -exec section of the man pages for find you will come across the {} string that allows you to use the matches as arguments within -exec. This will allow you to use rename on your find matches in the following way:
find . -name 'file_*' -exec rename 's/file_/mywish_/' {} \;
From the manual:
-exec command ;
Execute command; true if 0 status is returned. All following
arguments to find are taken to be arguments to the command until an
argument consisting of ;' is encountered. The string{}' is replaced
by the current file name being processed everywhere it occurs in the
arguments to the command, not just in arguments where it is alone, as
in some versions of find. Both of these constructions might need to
be escaped (with a `\') or quoted to protect them from expansion by
the shell. See the EXAMPLES section for examples of the use of the
-exec option. The specified command is run once for each matched file. The command is executed in the starting directory.There are
unavoidable security problems surrounding use of the -exec action;
you should use the -execdir option instead.
Although you asked for a find/exec solution, as Mark Reed suggested, you might want to consider piping your results to xargs. If you do, make sure to use the -print0 option with find and either the -0 or -null option with xargs to avoid unexpected behaviour resulting from whitespace or shell metacharacters appearing in your file names. Also, consider using the + version of -exec (also in the manual) as this is the POSIX spec for find and should therefore be more portable if you are wanting to run your command elsewhere (not always true); it also builds its command line in a way similar to xargs which should result in less invocations of rename.
Don't think there's a way you can do this with just find, you'll need to create a script:
#!/bin/bash
NEW=`echo $1 | sed -e 's/file_/mywish_/'`
mv $1 ${NEW}
THen you can:
find ./ -name 'file_*' -exec my_script {} \;

Shell Piping the data in middle of another command

If I need to have scripts like below:
find -name 'lib*.so' | xargs cp <files> ~/libs/.
Where < files > is the file which I found from the previous find command. Basically i want to pipe the data not just at the end but some where in the middle. (Some thing like $1 ???)
I understand I can have small sh file, where I can have this in a variable and use For loop & use that variable.... But what I want is simple one as I explained above. Where simple tasks can be accomplished easily.
Note: The script above is only a indication of type of Problem and the actual problem.
Let me know if this kind is possible.
If you just want to do copy
find -name 'lib*.so ' -print0 | xargs -r0 cp --target ~/libs/
You can do this using find only, without having to spawn cp(1) for each file by doing:
find -name 'lib*.so' -exec cp -t ~/libs {} +
Note that this only works with GNU cp and a POSIX 2008 compliant find, like GNU find.
I hope I understand what you're trying to do here...
You can do this using find only.
find -name 'lib*.so' -exec cp {} ~/libs/ \;

find and remove files with space using find command on Linux

I'm trying to remove all thumbs.db files in a Windows partition using find command in Ubuntu:
find . -iname "*.db"|while read junk;do rm -rfv $junk;done
But it's not working for me and nothing happens! I think I found the problem, the white spaces in directory names!
I did this trick to remove my junk files before on previous version of Ubuntu but now on latest version of Ubuntu I can't.
Is there any bug in my command?
I'd do it this way:
find . -iname 'thumbs.db' -exec rm -rfv {} +
This way, it still works even if your directories contain whitespace in their names.
just to throw this out there
find . -name "*.pyc" -delete
I'm not sure why you're using while.
find . -iname 'thumbs.db' -exec rm -rfv {} \;
...should suffice (and only delete the files you want to, not any BDB files that may be laying around).
The code looks good and works on arch and debian. Maybe there are no files matching "*.db"?
As a sidenote: I might not be a good idea to delete all files with the suffix ".db", because you can accidently delete other files than "Thumbs.db"
First check if the first part of your command, that is:
find . -iname "*.db"
is returning anything.
If it does then you can use xargs as follows to accomplish your task:
find . -iname "*.db" | xargs rm -rfv
UPDATE: From comments, this is unsafe, specially if there are spaces in directory/file names. You will need to use -print0 / xargs -0 to make it safe.

Why | doesn't work with find?

Why does the following command aiming to remove recursively all .svn folders
find . -name ".svn" | rm -rfv
doesn't work ?
I know the find command provides the -exec option to solve this issue but I just want to understand what is happening there.
In your example, the results from find are passed to rm's STDIN. rm doesn't expect its arguments in STDIN, though.
Here is an example how input redirecting works.
rm does not read file names from standard input, so any data piped to it is ignored.
The only thing it uses standard input for is checking whether it's a terminal, so it can determine whether to prompt.
It doesn't work because rm does not accept a list of file names on its standard input stream.
Just for reference, the safest way to handle this in the case of directories that might contain spaces is:
find . -name .svn -exec rm -frv {} \;
Or, if you are shooting for speed:
find . -name .svn -print0 | xargs -0 rm -frv
find do works with | ( for example find ~ -name .svn | grep "a") but the problem is with rm
This question is similar to this other answered question. Hope this helps.
How do I include a pipe | in my linux find -exec command?

How does one find and copy files of the same extension, in different directories, to a single directory in linux?

So, How Do I find and copy all files,
*.a
that are in,
~/DIR{1,2,3,...}
to
~/tmp/foo?
Assumed you meant recursively copy everything of type .a from some source location.
Haven't verified yet, but this should do that.
find <root-of-search> -type f -name '*.a' -exec cp {} /tmp/foo \;
replace with the top of wherever you want to search from. You might have to throw quotes around *.a, and you might have to replace escape the ending semicolon by putting it in single quotes rather than back-slashing it.
In a bash shell:
cp ~/DIR*/*.a ~/tmp/foo
find ~/DIR{1,2,...} -name *.a print0 | xargs -i -0 cp '{}' ~/tmp/foo

Resources