我有两个同名的目录:
$ ls mydir
file1 file2
$ ls other/mydir
file3 file4
如果我复制mydir
到other
,两个mydir
s 就会合并:
$ cp -r mydir other
$ ls other/mydir
file1 file2 file3 file4
在手册(或信息)页面的哪里cp
有说默认情况下会这样做?
如果我使用,也会发生同样的事情cp -rn mydir other
。
cp
如果问我是否要合并这两个,我会更喜欢mydir
;这样,如果我复制mydir
到other
而忘记了已经存在不同的mydir
in other
,我可以中止操作。这可能吗?
答案1
我没有在 GNU coreutils 手册中看到这一点。它指定为POSIX:
2.如果源文件如果是目录类型,则需要执行以下步骤:
[剪断当目标文件是现有目录时不适用于递归模式的步骤]
F。目录中的文件源文件应复制到该目录目标文件[…]
cp -rn
没有帮助,因为该-n
选项只说“不覆盖”,但合并目录不会覆盖任何内容。
我看不到任何选项rsync
或pax
对您有帮助。
您可以通过包装器来获得此行为cp
。不过,解析命令行选项很繁琐。未经测试的代码。已知问题:这不支持缩写的长选项。
function cp {
typeset source target=
typeset -a args sources
args=("$@") sources=()
while [[ $# -ne 0 ]]; do
case "$1" in
--target|-t) target=$2; shift args;;
--target=*) target=${1#*=};;
-t?*) target=${1#??};;
--no-preserve|--suffix|-S) shift;;
--) break;;
-|[^-]*) if [ -n "$POSIXLY_CORRECT" ]; then break; else sources+=($1); fi;;
esac
shift
done
sources+=("$@")
if [[ -z $target && ${#sources[@]} -ne 0 ]]; then
target=${sources[-1]}
unset sources[-1]
fi
for source in "${sources[@]}"; do
source=${source%"${source##*[^/]}"}
if [ -e "$target/${source##*/}" ]; then
echo >&2 "Refusing to copy $source to $target/${source##*/} because the target already exists"
return 1
fi
done
command cp "$@"
}
答案2
您可以创建一个用于复制目录 (cpDirs) 的包装脚本,该脚本将检查是否会发生任何合并:
#!/bin/sh
test -d "$1" && test -d "$2" || { >&2 echo "Not directories"; exit 1; }
conflicts="`for d in "$1" "$2"; do (cd "$d"; find -mindepth 1 -type d); done |
sort |uniq -d`"
if [ -n "$conflicts" ]; then
>&2 printf 'The following directories would be merged:\n%s\n' "$conflicts"
exit 1
else
cp -r "$@"
fi
答案3
cd /src/path &&
find . -type d ! -name . -prune \
\( -exec test -e /tgt/path/{} \; \
\( -ok echo cp -rt /tgt/path {} \; \
-o -exec printf 'not copied:\t%s\n' {} \; \
\) \) -o ! -name . -exec echo cp -rt /tgt/path {} +
find
的-ok
原始工作方式类似-exec
,只是它首先提示其 stderr,并提供其将要运行的命令的描述,并等待肯定或否定的响应(如y
或n
)然后输入。上面的find
脚本将提示确认是否存在目录/src/path
也存在于/tgt/path
在复制之前,但所有找到的文件都在/src/path
无需提示即可复制。
(不过,你必须删除echo
s 才能让它做任何事情,而不仅仅是假装工作)
另一个find
脚本调用下面一级目录的 shell/src/path
可能看起来像:
cd /src/path &&
find . ! -name . -prune -exec sh -c '
[ -t 0 ] &&
trap "stty $(stty -g;stty -icanon)
trap - 0 1 2; exit" 0 1 2
for f
do [ -e "$0/$f" ] &&
case $(printf "%b:\n%s\n" >&2 \
\\nsource "$(ls -ld -- "$PWD/$f")" \
\\ntarget "$(ls -ld -- "$0/$f")" \
"copy source over target?(\"y\"es|a\"no\"ther key): \c"
dd count=1 2>/dev/null
echo >&2) in ([yY]) ! :
esac|| set -- "$@" "$f"; shift
done; cp -r "$@" "$0"
' /tgt/path {} +