Wie kann verhindert werden, dass cp zwei Verzeichnisse mit identischem Namen zusammenführt?

7

Ich habe zwei gleichnamige Verzeichnisse:

$ ls mydir
file1 file2

$ ls other/mydir
file3 file4

Wenn ich kopieren mydirzu other, die beiden mydirbekommen s verschmolzen:

$ cp -r mydir other

$ ls other/mydir
file1 file2 file3 file4

Wo auf der Man- (oder Info-) Seite für cpsteht, dass dies standardmäßig geschieht?

Das gleiche passiert, wenn ich benutze cp -rn mydir other.

Ich würde es vorziehen, wenn cpich gefragt würde, ob ich die beiden zusammenführen möchte mydir. so dass , wenn ich kopieren mydirzu und otherdabei vergessen , dass es bereits eine andere mydirin other, konnte ich den Vorgang abbrechen. Ist das möglich?

EmmaV
quelle
Die Manpage von cp besagt, dass das Flag -n das Überschreiben vorhandener Dateien vermeidet und nichts über Verzeichnisse aussagt. Wie bei cp haben viele andere Befehle Flags, um das Überschreiben vorhandener Dateien zu vermeiden. Beispiele für solche Befehle sind tar (-k) und rsync (--ignore-existent). Leider kenne ich kein Kommandozeilen-Tool, das das Zusammenführen von Verzeichnissen beim rekursiven Kopieren vermeidet.
Henrik Carlqvist
2
Sie sind sich nicht sicher, ob Sie danach suchen, aber Sie können einen kleinen Test hinzufügen, bevor Sie den cpBefehl ausführen. if [ ! -d other/mydir] ; then; cp -r mydir other; else; echo "Directory already exists at destination"; fi
Munir
1
@ Munircontractor, ich denke, das ist in der Tat die beste Lösung. Es könnte ein wenig aufgeräumt werden, aber die Wahrheit ist, dass die Kernantwort auf die genaue Titelfrage lautet: "Sie können dies nicht verhindern cp, es sei denn, Sie überprüfen zuerst, also stellen Sie sicher, dass Sie zuerst überprüfen."
Wildcard
@ Munircontractor - Vielen Dank für eine einfache Lösung. Das einzige Problem ist, dass es einfacher ist ls, dieses Skript manuell zu überprüfen, als es jedes Mal zu bearbeiten.
EmmaV

Antworten:

5

Ich sehe dies nicht im Handbuch von GNU Coreutils dokumentiert. Es wird von POSIX angegeben :

2. Wenn die Quelldatei vom Typ Verzeichnis ist, müssen die folgenden Schritte ausgeführt werden:

[ Snip- Schritte, die im rekursiven Modus nicht angewendet werden, wenn die Zieldatei ein vorhandenes Verzeichnis ist]

    f. Die Dateien im Verzeichnis source_file wird in das Verzeichnis kopiert werden dest_file [...]

cp -rnhilft nicht, weil die -nOption nur "Nicht überschreiben" sagt, aber das Zusammenführen von Verzeichnissen nichts überschreibt.

Ich sehe keine Option dafür rsyncoder paxdas würde dir helfen.

Sie können dieses Verhalten mit einem Wrapper erhalten cp. Das Parsen der Befehlszeilenoptionen ist jedoch umständlich. Ungetesteter Code. Bekanntes Problem: Dies unterstützt keine abgekürzten langen Optionen.

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 "$@"
}
Gilles 'SO - hör auf böse zu sein'
quelle
Warten Sie, was shift args?
Arthur2e5
@ Arthur2e5 Sorry, zshism. Ich habe das und ein paar andere Fehler behoben.
Gilles 'SO - hör auf böse zu sein'
Vielen Dank. Ich glaube, ich bin verpflichtet, dies zu akzeptieren, weil Sie offensichtlich große Anstrengungen unternommen haben.
EmmaV
1

Sie können ein Wrapper-Skript zum Kopieren von Verzeichnissen (cpDirs) erstellen, das prüft, ob Zusammenführungen auftreten:

#!/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
PSkocik
quelle
0
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 {} +

findDas Grundelement von ' -okfunktioniert so, -execaußer dass es zuerst mit einer Beschreibung des Befehls, den es ausführen soll, zu seinem stderr auffordert und auf eine positive oder negative Antwort (wie yoder n) wartet, gefolgt von einer Eingabe. Das obige findSkript fordert vor dem Kopieren zur Bestätigung auf, ob auch ein Verzeichnis in /src/pathvorhanden /tgt/pathist, aber alle gefundenen Dateien in /src/pathwerden ohne Aufforderung kopiert.

(Sie müssen das echos entfernen , damit es mehr als nur so tut, als würde es funktionieren.)

Ein anderes findSkript, das eine Shell für Verzeichnisse der ersten Ebene aufruft, /src/pathsieht möglicherweise folgendermaßen aus:

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 {} +
mikeserv
quelle