在find -exec中同时使用basename和完整路径

10

今天我正在通过find脚本探索冒险世界。

假设我想要将/home/mine/Pictures的任何子目录中的png文件复制到/home/mine/pngcoppies,并将其重命名为“copy [basename]”,我需要使用find和-exec。 这将要求我在同一exec命令中同时使用完整路径名和basename. 但我不知道如何获取basename。(见下文)

find /home/mine -iname "*.png" -exec cp {} /home/mine/pngcoppies/copy{what_do_I_enter_here?} \;

注意:上述并不是我正在做的实际操作,但它是问题的基本示例,因此使用其他方法来实现相同目的的解决方法并不适用于这里。这个问题的根本在于find -exec及其对基名的使用。

提前感谢您的帮助!

5个回答

19

来自于 man find 命令的说明:

-execdir 是 -exec 的变体,唯一的不同之处在于命令会从包含当前文件的目录中执行。字符串 "{}" 代表的文件名没有经过修饰。

find /home/mine -iname "*.png" -execdir cp {} /home/mine/pngcoppies/copy{} \;

1
这是适合我的一个。 - Matt Sephton
1
@Makkou的回答很好,对于POSIX历史上最令人发狂的现象之一进行了出色的解释,但这个方法可以使代码更易读,并且可以解决大量情况下的问题。然而,对于那些边缘情况,记住更通用的解决方案也是很好的。 - cycollins

19

要查看执行find命令时发生了什么,只需要键入set -xv
-x:在执行命令及其参数时打印它们。
-v:在读取shell输入行时打印它们。

这是我拥有的内容:

find . -name "*.xml" -exec echo {} \;

给出输出结果:

./log.xml
./svnLog.xml

当我尝试时:

set -xv
find . -name "*.xml" -exec echo {} \;

我理解为:

find . -name "*.xml" -exec echo {} \;
+ find . -name '*.xml' -exec echo '{}' ';'
./log.xml
./svnLog.xml

然后找到执行 echo 命令,传递找到的文件名而不是字面值:'{}'

但是当您像下面这样将一些内容添加到 {} 中时:

find . -name "*.xml" -exec echo something{} \;
+ find . -name '*.xml' -exec echo 'something{}' ';'
something{}
something{}

这里针对我拥有的2个XML文件执行了两次回显,由于'{}'不再是exec的参数列表中,它不会被替换。因此我们得到了每个找到的文件的echo 'something{}'

为了处理这个问题,你可以考虑像这样将文件名作为参数传递给echo:

sh -xvc 'echo sothing/$0' filename

我们已经知道什么是-x-v-c的作用是从其后面的字符串中获取命令 (man sh)。

所以结果是:

sh -xvc 'echo somthing/$0' filename
+ sh -xvc 'echo somthing/$0' filename
echo somthing/$0
+ echo somthing/filename
sothing/filename

我在单引号中使用 'echo somthing/$0',以防止 $0 被当前 shell 展开。用双引号试试,你会看到 $0 被展开了 ;)

所以,回到你的 '问题',find 命令应该按以下格式进行格式化:

find . -name "*.xml" -exec sh -xvc 'echo sothing/$0' {} \;

然后我们将得到:

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

正如我们现在所看到的,find命令将执行shell命令echo something/$0,并传递'{}'(由find找到的文件名替换)以便我们得到期望的echo something/./log.xml

set +xv用于关闭详细模式,我们可以得到:

find . -name "*.xml" -exec sh -c 'echo "cp $0 someWhereElse/$0"' {} \;
cp ./log.xml someWhereElse/./log.xml
cp ./svnLog.xml someWhereElse/./svnLog.xml

所以在你的情况下,你只需要在子shell中执行复制操作(在exec之后添加shbash或您最喜欢的shell),并让find将文件名作为参数传递给它;)

find /home/mine -iname "*.png" -exec sh -c 'cp $0 /home/mine/pngcoppies/copy/$0' {} \;

希望这可以帮到您,对我的英语表达请谅解。


这个子shell的建议很好!我建议引用"$0"以允许包含空格。 - Jonas Eberle

2
尝试像这样做:

尝试像这样做:

find  /home/mine -iname "*.png" -printf "%P\n " | xargs  -I % -n1 cp %  /home/mine/pngcoppies/copy% 

我非常喜欢这种方法,但在OSX上我出现了“find: -printf: unknown primary or operator”错误。 - spuder

0

要获取基本名称,您可以使用

basename $your_full_path

获取基本名称之前的路径
dirname $your_full_path

4
抱歉要求你为我提供详细指导,但我应该如何将这个方法应用到我的查找实例中?(请注意,此翻译已经尽可能地使内容通俗易懂,但并未改变原意,并且没有包含任何额外的解释或信息。) - neanderslob

0

你需要将前两个答案结合起来

我正在使用一个命令将一个目录中的所有内容复制到另一个目录中

find . -type f -exec sh -c 'tail -n 1000 $0 >../tail1000/$0.tail' {} \;

第一个答案给出了

cp ./log.xml someWhereElse/./log.xml

或者是针对我的tail命令

find . -type f -exec sh -c 'tail -n 1000 $0 >../tail1000/$0.tail' {} \;

tail -n 1000 ./filenane > ../tail1000/./filenane.tail

这个方法似乎可行,但路径看起来不太好,我预计在某些情况下,路径/./morepath可能会对某些命令产生意外的影响。

将这些答案结合起来得到:

find . -type f -execdir sh -c 'tail -n 1000 $0 >../tail1000/$0.tail' {} \;

执行

tail -n 1000 filenane > ../tail1000/filenane.tail

看起来更有可能给出预期的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接