I'm having an adventure in the world of bash scripting with find today.
Say I'm looking to copy any png file in any subdirectory of /home/mine/Pictures to /home/mine/pngcoppies and rename it "copy[basename]"using find and -exec. This would require me to use both the full path name and the basename in the same exec command. My problem is that I don't know how to get the basename. (See below)
find /home/mine -iname "*.png" -exec cp {} /home/mine/pngcoppies/copy{what_do_I_enter_here?} \;
Note: The above isn't actually what I'm doing, but it's a fundamental example of the issue, so a workaround using some other method to achieve the same ends wouldn't really apply here. The question is fundamentally about find -exec and its use of basenames.
Thanks in advance!
To see what is going on when you execute the find, just type set -xv
-x : Prints commands and their arguments as they are executed.
-v : Prints shell input lines as they are read.
Here is what I have :
find . -name "*.xml" -exec echo {} \;
Gives the output:
./log.xml
./svnLog.xml
And when I try :
set -xv
find . -name "*.xml" -exec echo {} \;
I get :
find . -name "*.xml" -exec echo {} \;
+ find . -name '*.xml' -exec echo '{}' ';'
./log.xml
./svnLog.xml
And then find execute echo passing the found filename instead of the litteral : '{}'
but when you add something to the {}
like below :
find . -name "*.xml" -exec echo something{} \;
+ find . -name '*.xml' -exec echo 'something{}' ';'
something{}
something{}
Here the echo is executed twice for the 2 xml files that I have and since there is no more '{}'
is the parameter list of the exec, it is not going to be replaced. so we got the echo 'something{}'
for each file found.
To deal with this, you can think about executing echo passing to it the filename as parameter like for example :
sh -xvc 'echo sothing/$0' filename
We already know what is -x
and -v
. -c
is to get the command from the string after it (man sh
)
so the result is :
sh -xvc 'echo somthing/$0' filename
+ sh -xvc 'echo somthing/$0' filename
echo somthing/$0
+ echo somthing/filename
sothing/filename
I used 'echo somthing/$0
' between ' ' so that $0 don't get expanded by the current shell. try it with " " and you will see the expantion of $0 ;)
So to get back to your 'problem', the find should be formatted as below:
find . -name "*.xml" -exec sh -xvc 'echo sothing/$0' {} \;
And we will get :
find . -name "*.xml" -exec sh -xvc 'echo sothing/$0' {} \;
+ find . -name '*.xml' -exec sh -xvc 'echo sothing/$0' '{}' ';'
echo sothing/$0
+ echo sothing/./log.xml
sothing/./log.xml
echo sothing/$0
+ echo sothing/./svnLog.xml
sothing/./svnLog.xml
As we can see know, the find is going to execute the shell cammand echo sothing/$0
passing to it '{}'
(replaced by the filename found by find) so we get the desired echo sothing/./log.xml
set +xv
to remove the verbose mode
and we can get :
find . -name "*.xml" -exec sh -c 'echo "cp $0 someWhereElse/$0"' {} \;
cp ./log.xml someWhereElse/./log.xml
cp ./svnLog.xml someWhereElse/./svnLog.xml
so in your case , you have just to execute the copy in a sub shell (add sh
or bash
or you favorit shell after the exec) and let find
pass the filename as parapeter to the it ;)
find /home/mine -iname "*.png" -exec sh -c 'cp $0 /home/mine/pngcoppies/copy/$0' {} \;
Hope this can help, and execuse me for my English.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With