没有覆盖-n被cp命令

时间:2015-06-09 16:47:22

标签: linux macos bash shell unix

我尝试cp OSX桌面上符合五位数模式的某些文件。它有效,但我无法理解为什么-n选项被忽略。如果文件已经存在,我不想覆盖文件。

find ./prefix* -name '[0-9][0-9][0-9][0-9][0-9]' -maxdepth 5 -exec cp -nr {} ./dest \; 

即使一个目录已在dest中,也会复制所有内容。我怎么强迫不覆盖?超级用户This solution表示我可以简单地将dest中所有内容的权限更改为只读。但我觉得必须有一个理由说明我的实现cpman页面上的实现不一致,因此应该有更好的方法来解决问题。

此外,被覆盖文件的权限为rwxr-xr-x(如果是你的话,则为0755。)

1 个答案:

答案 0 :(得分:1)

cp不会做你想做的事。您需要迭代find的输出。假设您找不到任何路径中的空格或其他特殊字符(如果您这样做,请参见下文):

find ./prefix* -name '[0-9][0-9][0-9][0-9][0-9]' -maxdepth 5 | \
while read dir
do
    target=./dest/$(basename $dir)
    [ -d $target ] || cp -r $dir ./dest/
done

这是有效的,因为只要while返回成功,do就会继续执行doneread之间的内容。 find的输出通过管道传输到read,因此每次执行read dir时,它都会从find中读取一行输出并将其分配给dir变量。

当没有更多行要从find读取时,read返回失败并且循环终止。

在循环体内,basename打印传递给它的路径的最后部分。在这种情况下,5位数。

[ ... ]是用于运行条件测试的shell术语。 (是的,[是一个命令!)您可以说test -d ...而不是[ -d ... ]。参见例如https://unix.stackexchange.com/questions/99185/what-do-square-brackets-mean-without-the-if-on-the-left了解更多相关信息)

如果参数作为目录存在,

-d ...将返回成功。如果没有失败。

||表示或 - 所以foo || bar仅在bar失败时执行foo

所以循环体基本上说:

let target be "dest/" + the basename of $dir
such a directory exists or copy $dir into dest/

我希望稍微澄清一下。用很少的代码就可以搞很多shell语言。所有这些信息基本上都在bash联机帮助页中,但可以说是一种更难以访问的格式。

如果find找到的任何路径都包含空格或其他特殊字符,那么您需要添加引号和其他一些铃声&口哨:

find ./prefix* -name '[0-9][0-9][0-9][0-9][0-9]' -maxdepth 5 -print0 | \
while IFS= read -r -d '' dir
do
    target="./dest/$(basename "$dir")"
    [ -d "$target" ] || cp -r "$dir" ./dest/
done