cp가 동일한 이름을 가진 두 디렉토리를 병합하는 것을 방지하는 방법은 무엇입니까?

cp가 동일한 이름을 가진 두 디렉토리를 병합하는 것을 방지하는 방법은 무엇입니까?

동일한 이름을 가진 두 개의 디렉토리가 있습니다.

$ ls mydir
file1 file2

$ ls other/mydir
file3 file4

mydir에 복사하면 other두 개의 mydir가 병합됩니다.

$ cp -r mydir other

$ ls other/mydir
file1 file2 file3 file4

매뉴얼(또는 정보) 페이지 어디에 cp이것이 기본적으로 수행된다고 나와 있습니까?

을 사용해도 같은 일이 발생합니다 cp -rn mydir other.

cp나는 두 개를 병합할지 묻는 질문을 받는 것을 선호합니다. mydir그러면 복사 mydirother후 다른 항목이 mydir이미 존재한다는 사실을 잊어버린 경우 other작업을 중단할 수 있습니다. 가능합니까?

답변1

GNU coreutils 매뉴얼에서는 이것을 볼 수 없습니다. 다음과 같이 지정됩니다.POSIX:

2. 만약에소스 파일디렉터리 유형인 경우 다음 단계를 수행해야 합니다.

[전단대상 파일이 기존 디렉터리인 경우 재귀 모드에 적용되지 않는 단계]

    에프. 디렉토리의 파일소스 파일이 디렉토리에 복사해야 합니다대상 파일[…]

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스크립트는 한 수준 아래 디렉터리의 셸을 호출합니다./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 {} +

관련 정보