如何防止 cp 合并两个同名目录?

如何防止 cp 合并两个同名目录?

我有两个同名的目录:

$ ls mydir
file1 file2

$ ls other/mydir
file3 file4

如果我复制mydirother,两个mydirs 就会合并:

$ cp -r mydir other

$ ls other/mydir
file1 file2 file3 file4

在手册(或信息)页面的哪里cp有说默认情况下会这样做?

如果我使用,也会发生同样的事情cp -rn mydir other

cp如果问我是否要合并这两个,我会更喜欢mydir;这样,如果我复制mydirother而忘记了已经存在不同的mydirin other,我可以中止操作。这可能吗?

答案1

我没有在 GNU coreutils 手册中看到这一点。它指定为POSIX:

2.如果源文件如果是目录类型,则需要执行以下步骤:

[剪断当目标文件是现有目录时不适用于递归模式的步骤]

    F。目录中的文件源文件应复制到该目录目标文件[…]

cp -rn没有帮助,因为该-n选项只说“不覆盖”,但合并目录不会覆盖任何内容。

我看不到任何选项rsyncpax对您有帮助。

您可以通过包装器来获得此行为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,并提供其将要运行的命令的描述,并等待肯定或否定的响应(如yn然后输入。上面的find脚本将提示确认是否存在目录/src/path也存在于/tgt/path在复制之前,但所有找到的文件都在/src/path无需提示即可复制。

(不过,你必须删除echos 才能让它做任何事情,而不仅仅是假装工作)

另一个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 {} +

相关内容