【问题标题】:No overwrite -n being ignored by cp commandcp 命令不会忽略覆盖 -n
【发布时间】:2015-06-09 16:47:21
【问题描述】:

我正在尝试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,如果你喜欢八进制)。

【问题讨论】:

  • 您确定-n 适用于目录吗?手册页只讨论文件。
  • @AndreasGrapentin 叹息。好点子。
  • @AndreasGrapentin 关于如何使其与目录一起工作的任何想法?
  • 不幸的是没有,否则我会发布答案。但也许你可以让它与 rsync 一起工作?如果没有,您可能必须用 python 或类似的东西编写一个小程序,这正是您想要的。
  • 我自己会(或者更确切地说,)走rsync路线。

标签: linux macos bash shell unix


【解决方案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

这是可行的,因为只要read 返回成功,while 就会继续执行dodone 之间的内容。 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 仅在 foo 失败时执行 bar

所以循环体基本上说:

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

【讨论】:

  • 解释和阐述。希望对您有所帮助。
  • 这不适用于给定的所有有效 UNIX 文件名。 read 需要 -r 处理带有反斜杠的文件名,IFS 需要清除以处理以空格结尾的文件名,并且在流是面向行的情况下根本无法处理带有换行符的文件名。
  • 更好 - 处理所有极端情况 - 将在 find 中使用 -print0 操作,在 bash 端使用 while IFS= read -r -d '' dir
  • 另外,还有一堆引用错误。 [ -d $target ] 必须是 [ -d "$target" ],例如; target=dest/$(basename $dir) 需要为target=dest/$(basename "$dir")(或target=dest/${dir%/*},这样效率更高);等
  • ...由于这些名称受find 表达式的限制,以上内容无法通过此处的文件名加以利用,但是(1)StackOverflow 中给出的答案会被具有不同用例的其他人重用,并且约束; (2) 当这些目录包含匹配的文件时,它们目录名称利用。
猜你喜欢
  • 2011-04-10
  • 1970-01-01
  • 1970-01-01
  • 2016-05-07
  • 2020-03-30
  • 2015-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多