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 片段的模式,这是另一种制作临时副本,然后将其移动到位的方法。我相信这种方法不易受到任何竞争条件的影响。它确实需要-n
on 标志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
实现那样复制权限、所有权或稀疏性)。