如果目标存在,则使 cp 返回错误值

如果目标存在,则使 cp 返回错误值

cp如果目标文件已经存在,有没有办法让(从 Linux 上的 GNU coreutils)返回非零值?
或者是否有任何其他常用的小实用程序可以提供此功能?

这在 shell 脚本中非常有用,可以自动复制文件而不会意外丢失任何内容,并且无需用户交互。通过将副本与原始版本进行比较可以获得类似的结果,但我希望有一个更简单的解决方案。

答案1

某些cp实现(包括 GNU cp)具有非标准-n开关,可以不破坏文件(如果存在)。但 GNU无论如何cp都会返回。0

您可以在 shell 脚本中使用 if 语句来测试文件是否存在,然后再向其中复制内容:

if [ -e /path/to/file ]
  then 
   exit 1
  else
   cp file /path/to/file
fi

或者如果你想要一个函数,你可以使用这样的东西:

function cpa(){ 
 if [ -e "$2" ]
  then 
   exit 1
  else 
   /bin/cp "$1" "$2"
  fi 
}

答案2

以下 shell 片段仅在目标不存在时复制文件,如果目标存在则返回非零状态。

(set -C; : >"$target" && cp -- "$source" "$target")

这不是原子的:如果在尝试破坏之后$target且复制开始之前创建文件,则该文件将被覆盖。此外,cp它不是原子的:另一个进程可以观察正在写入的文件。然而,如果所有并发编写者使用相同的策略,这确实可以正常工作。

以下更复杂的代码片段首先创建一个临时副本,然后将其移动到位。仍然存在竞争条件:有一个很小的时间窗口,在此期间不运行的不相关程序可以创建目标文件。

( set -C
  tmp=$(mktemp -p "$(dirname -- "$target")")
  cp -- '$source" "$tmp"
  if : >"$target"; then
    mv -i -- "$tmp" "$target"
  else
    rm -- "$tmp"
    false
  fi
)

答案3

扩展我对 h3rrmiller 答案的评论,并遵循 Gilles 片段的模式,这是另一种制作临时副本,然后将其移动到位的方法。我相信这种方法不易受到任何竞争条件的影响。它确实需要-non 标志mv,这不是 POSIX 规范的一部分。然而,它似乎确实被广泛使用。 (已在 GNU 实现以及 BusyBox、FreeBSD、OS X 上进行验证。)

(
  tmp=$(mktemp "${target}.XXXXXX") && # "mktemp -p" doesn't work on BSD
  cp -- "$source" "$tmp" && # you may want to add -p flag
  inode=$(ls -di "$tmp" | cut -d' ' -f1) && # see comment below
  mv -n -- "$tmp" "$target" && # won't overwrite, but doesn't then exit non-0
  if [ "$(ls -di "$target" | cut -d' ' -f1)" != "$inode" ]; then
    rm -- "$tmp"; false
  fi
)

在 Linux 中,您可以使用 获取 inode stat -c'%i' "$tmp",但是那不是便携式的

另一个想法是检查路径中是否仍然存在文件,而不是验证 inode $tmp。如果mv -n ...成功的话就不应该有。 (除非$tmp在您检查是否丢失之前已经新创建了其他进程。)

$target为了提高效率,在例程开始时添加检查是否存在是有意义的。如果我们发现它确实存在,我们可能会立即失败,而不是制作副本,尝试移动它,然后看到失败,然后删除副本。这是一个(简单的)练习。

答案4

你可以这样做:

(set -C &&  cat < /path/to/src > /path/to/dest)

但它不会复制除文件内容之外的任何内容(不是像某些cp实现那样复制权限、所有权或稀疏性)。

相关内容