Linux: Kopieren und erstellen Sie das Zielverzeichnis, falls es nicht vorhanden ist

347

Ich möchte einen Befehl (oder wahrscheinlich eine Option für cp), der das Zielverzeichnis erstellt, wenn es nicht vorhanden ist.

Beispiel:

cp -? file /path/to/copy/file/to/is/very/deep/there
Flybywire
quelle
5
Akzeptierte Antwort ist falsch (es gibt eine solche Option für cp). Siehe zweite Antwort von Paul Whipp.
Ronenz
1
@ Ronenz, ich stimme zu, dass es eine erwähnenswerte Option in GNU gibt cp(wie von Paul Whipp beantwortet), aber sie ist nicht so allgemein wie vom OP angefordert. Es kann kopieren a/foo/bar/filezu , b/foo/bar/fileaber es kann nicht kopieren filezu b/foo/bar/file. (dh es funktioniert nur, um übergeordnete Verzeichnisse zu erstellen, die bereits im Fall der ersten Datei vorhanden waren, aber es können keine beliebigen Verzeichnisse erstellt werden)
Sudo Bash
Das sieht nach einer netten Feature-Anfrage für cp aus. Gibt es eine Chance, dass wir in naher Zukunft einen solchen Befehlszeilenschalter bekommen?
Arnaud

Antworten:

327
mkdir -p "$d" && cp file "$d"

(Es gibt keine solche Option für cp).

Michael Krelin - Hacker
quelle
62
Ich glaube nicht, dass Sie das brauchen test -d: ist mkdir -pimmer noch erfolgreich, auch wenn das Verzeichnis bereits existiert.
Ephemient
Ups, richtig. Aber dann kann Test eine eingebaute Bash sein (besonders wenn als [[-d "$ d"]] geschrieben) und mkdir kann nicht ;-)
Michael Krelin - Hacker
1
mkdirist ein eingebautes in BusyBox;)
Ephemient
7
@holms $dist eine Variable, die vermutlich das betreffende Verzeichnis enthält. Ich meine, wenn Sie es dreimal wiederholen müssen, werden Sie es wahrscheinlich zuerst der Variablen zuweisen.
Michael Krelin - Hacker
237

Wenn beide der folgenden Aussagen zutreffen:

  1. Sie verwenden die GNU-Version von cp(und nicht beispielsweise die Mac-Version) und
  2. Sie kopieren aus einer vorhandenen Verzeichnisstruktur und müssen diese nur neu erstellen

dann können Sie dies mit der --parentsFlagge von tun cp. Von der Infoseite (sichtbar unter http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html#cp-invocation oder mit info cpoder man cp):

--parents
     Form the name of each destination file by appending to the target
     directory a slash and the specified name of the source file.  The
     last argument given to `cp' must be the name of an existing
     directory.  For example, the command:

          cp --parents a/b/c existing_dir

     copies the file `a/b/c' to `existing_dir/a/b/c', creating any
     missing intermediate directories.

Beispiel:

/tmp $ mkdir foo
/tmp $ mkdir foo/foo
/tmp $ touch foo/foo/foo.txt
/tmp $ mkdir bar
/tmp $ cp --parents foo/foo/foo.txt bar
/tmp $ ls bar/foo/foo
foo.txt
Paul Whipp
quelle
4
Dies funktioniert nicht unter Mac OS X, daher denke ich, dass es Linux-spezifisch ist.
Olt
7
Ich würde gnu-spezifisch sagen. Wenn Sie haben macports, können Sie coreutils und seine installieren gcp.
Michael Krelin - Hacker
16
Mann, Linux hat alles
Ziggy
19
Ich denke, dies ist die Antwort auf eine etwas andere Frage, obwohl es die Antwort ist, nach der ich gesucht habe (und daher die, die ich positiv bewertet habe). Ich denke, dies ist die Lösung, vorausgesetzt, Sie möchten, dass die übergeordneten
Zielverzeichnisse
7
Dies setzt voraus, dass das Verzeichnis 'bar' bereits vorhanden ist. Die ursprüngliche Frage impliziert jedoch, wie automatisch 'bar' erstellt wird, wenn es als Ziel angegeben wird und noch nicht vorhanden ist. Die Antwort darauf liefert @ MichaelKrelin-Hacker.
Donnerstag,
69

Kurze Antwort

So kopieren myfile.txtzu /foo/bar/myfile.txtverwenden:

mkdir -p /foo/bar && cp myfile.txt $_

Wie funktioniert das?

Da dies einige Komponenten enthält, werde ich Schritt für Schritt die gesamte Syntax behandeln.

Das im POSIX-Standard angegebene Dienstprogramm mkdir erstellt Verzeichnisse. Das Argument, pro der Dokumentation, wird dazu führen , mkdir zu-p

Erstellen Sie fehlende Komponenten für den Zwischenpfadnamen

dass bedeutet , wenn Sie anrufen mkdir -p /foo/bar, mkdir schaffen wird /foo und /foo/bar wenn /fookeine vorhanden ist. (Ohne -pwird stattdessen ein Fehler ausgegeben.

Der &&Listenoperator, wie im POSIX-Standard (oder im Bash-Handbuch, wenn Sie dies bevorzugen) dokumentiert , hat den Effekt, dass er cp myfile.txt $_nur ausgeführt wird, wenn er mkdir -p /foo/barerfolgreich ausgeführt wird. Dies bedeutet, dass der cpBefehl nicht versucht, ausgeführt zu werden, wenn er mkdiraus einem der vielen Gründe fehlschlägt .

Schließlich ist das, das $_wir als zweites Argument übergeben, cpein "spezieller Parameter", der nützlich sein kann, um zu vermeiden, dass lange Argumente (wie Dateipfade) wiederholt werden, ohne dass sie in einer Variablen gespeichert werden müssen. Gemäß dem Bash-Handbuch gilt Folgendes:

Erweitert sich zum letzten Argument des vorherigen Befehls

In diesem Fall haben /foo/barwir das weitergegeben mkdir. Der cpBefehl wird also erweitert und in das neu erstellte Verzeichnis cp myfile.txt /foo/barkopiert .myfile.txt/foo/bar

Beachten Sie, dass $_ist nicht Teil des POSIX - Standard , so dass theoretisch eine Unix - Variante könnte eine Schale hat , die nicht dieses Konstrukt nicht unterstützt. Ich kenne jedoch keine modernen Muscheln, die nicht unterstützt werden $_. sicherlich tun es Bash, Dash und zsh alle.


Ein letzter Hinweis: Der Befehl, den ich zu Beginn dieser Antwort gegeben habe, setzt voraus, dass Ihre Verzeichnisnamen keine Leerzeichen enthalten. Wenn Sie mit Namen mit Leerzeichen arbeiten, müssen Sie sie so zitieren, dass die verschiedenen Wörter werden nicht als unterschiedliche Argumente zu mkdiroder behandelt cp. Ihr Befehl würde also tatsächlich so aussehen:

mkdir -p "/my directory/name with/spaces" && cp "my filename with spaces.txt" "$_"
Mark Amery
quelle
16
Nebenbei: Ich bin normalerweise enttäuscht über den Mangel an Details bei Fragen zu Bash- oder Unix-Shell-Befehlen in Stack Overflow, die häufig einen komplizierten und extrem dichten Einzeiler auslösen, der schwer zu entfernen ist, wenn Sie noch kein Unix-Assistent sind. Ich habe versucht, hier das entgegengesetzte Extrem zu wählen und beide erklären jedes Detail der Syntax und verlinken auf die entsprechenden Stellen, um Dokumentation darüber zu finden, wie dieses Zeug funktioniert. Ich hoffe das hilft zumindest einigen Leuten. Gutschrift, wo fällig: Ich habe diesen Ansatz aus dieser Antwort auf eine doppelte Frage geklaut
Mark Amery
Ich habe $ _ noch nie gesehen. Worauf bezieht es sich? der im letzten Befehl verwendete Ordner?
Thebunnyrules
9
@thebunnyrules Aber ... aber ... es gibt ein "Wie funktioniert das?" Abschnitt in meiner Antwort, der buchstäblich mehrere Absätze enthält, die speziell der Frage gewidmet sind, die Sie gerade gestellt haben. Es fühlt sich ignoriert und ungeliebt an. :(
Mark Amery
Das tut mir leid. Du hast absolut recht. Ich wusste bereits alles andere in Ihrer Antwort, also habe ich es schnell durchgesehen. Ich hätte es genauer lesen sollen.
Thebunnyrules
Wow, du hast wirklich viel Arbeit in diese Antwort gesteckt ... Danke dafür. Es war schön zu lernen über: $ _. Ich kann mir vorstellen, dass ich es von nun an ziemlich oft benutze. Ich habe ein Skript geschrieben, um den gesamten Prozess zu automatisieren und in diesen Thread zu stellen. Probieren
thebunnyrules
39

Eine so alte Frage, aber vielleicht kann ich eine alternative Lösung vorschlagen.

Mit dem installProgramm können Sie Ihre Datei kopieren und den Zielpfad "on the fly" erstellen.

install -D file /path/to/copy/file/to/is/very/deep/there/file

Es sind jedoch einige Aspekte zu berücksichtigen:

  1. Sie müssen auch den Namen der Zieldatei angeben , nicht nur den Zielpfad
  2. Die Zieldatei ist ausführbar (zumindest soweit ich dies bei meinen Tests gesehen habe).

Sie können die Nummer 2 einfach ändern, indem Sie die -mOption zum Festlegen von Berechtigungen für die Zieldatei hinzufügen (Beispiel: -m 664Erstellt die Zieldatei mit Berechtigungen rw-rw-r--, genau wie das Erstellen einer neuen Datei mit touch).


Und hier ist es die schamlose Verbindung zu der Antwort, von der ich inspiriert wurde =)

help_asap
quelle
Hat für meinen Gebrauch nicht wirklich funktioniert, aber cooler Trick! Ich werde daran denken.
Thebunnyrules
Beachten Sie, dass dieser Befehl die Zieldateien mit 755( rwxr-xr-x) Berechtigungen erstellt, was wahrscheinlich nicht erwünscht ist. Sie können etwas anderes mit dem angeben -mSchalter, aber ich konnte nicht einen Weg finden nur halten Sie die Dateiberechtigungen :(
törzsmókus
19

Shell-Funktion, die macht, was Sie wollen, und sie als "Begräbnis" -Kopie bezeichnet, weil sie ein Loch für die Datei gräbt, in der sie leben soll:

bury_copy() { mkdir -p `dirname $2` && cp "$1" "$2"; }
Andy Ross
quelle
1
Sie sollten Anführungszeichen um den haben dirname $2zu
Tarnay Kálmán
4
@ Kalmi, für ein korrektes Zitat möchten Sie auch $2als Argument zitieren dirname. Wie mkdir -p "$(dirname "$2")". Ohne diese Angabe $2in cpnützt nichts;)
Michael Krelin - Hacker
3
Beachten Sie, dass diese Antwort davon ausgeht, dass $ 2 nicht bereits das Zielverzeichnis ist, andernfalls möchten Sie nur "mkdir -p" $ 2 "&& cp" $ 1 "" $ 2 "als Funktionskörper
user467257
Ich denke, das hat einen Fehler, wie @ user467257 hervorhebt. Ich denke nicht, dass dies behoben werden kann, da $ 2 in diesem Szenario zwischen dem Zielverzeichnis und der Zieldatei nicht eindeutig ist. Ich habe eine alternative Antwort gepostet, die dies anspricht.
Rich
@ Rich, ich bin nicht einverstanden, dass dies nicht reparierbar ist. Folgendes funktioniert sowohl für Dateien als auch für Verzeichnisse einwandfrei: mkdir -p dirname "$2"&& cp -r "$ 1" "$ 2"; Beachten Sie, dass die Backticks dirname $2nicht angezeigt werden, da SO sie als Codemarkierungen analysiert.
Yury
11

Hier ist eine Möglichkeit, dies zu tun:

mkdir -p `dirname /path/to/copy/file/to/is/very/deep/there` \
   && cp -r file /path/to/copy/file/to/is/very/deep/there

dirnamegibt Ihnen das übergeordnete Element des Zielverzeichnisses oder der Zieldatei. mkdir -p `dirname ...` erstellt dann dieses Verzeichnis und stellt sicher, dass beim Aufrufen von cp -r das richtige Basisverzeichnis vorhanden ist.

Der Vorteil dieser Übereltern besteht darin, dass sie für den Fall funktionieren, dass das letzte Element im Zielpfad ein Dateiname ist.

Und es wird unter OS X funktionieren.

Jamie McCrindle
quelle
6

install -D file -m 644 -t /path/to/copy/file/to/is/very/deep/there

Spongman
quelle
1
Eigentlich ist es überhaupt nicht dasselbe. Hierfür müssen Sie den Dateinamen zweimal übergeben (daher das Flag '-t') und die Ausführungsbits für die Datei setzen (daher das Beispiel '-m'). Dies sollte nicht die beste Antwort sein, da es die Frage nicht wirklich beantwortet - während meine es tut.
Spongman
1
Dies funktioniert für eine einzelne Datei. Berücksichtigen Sie einfach, dass dies theredie endgültige Datei und nicht das endgültige Unterverzeichnis ist.
kvantour
6

Bei allem Respekt für die obigen Antworten bevorzuge ich die Verwendung von rsync wie folgt:

$  rsync -a directory_name /path_where_to_inject_your_directory/

Beispiel:

$ rsync -a test /usr/local/lib/
Marcdahan
quelle
Ich habe es versucht und folgendes Ergebnis erzielt: rsync -a bull / some / hoop / ti / doop / ploop ERGEBNIS rsync: mkdir "/ some / hoop / ti / doop / ploop" fehlgeschlagen: Keine solche Datei oder Verzeichnis (2) rsync-Fehler : Fehler in Datei IO (Code 11) bei main.c (675) [Receiver = 3.1.2]
thebunnyrules
@thebunnyrules the Sie müssen den Pfad mit dem Befehl pwd überprüfen (siehe: [link] ( access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/… )) und ihn korrekt in Ihre Anweisung
einfügen
Wenn ich Ihren Vorschlag richtig verstehe, schlagen Sie vor, dass ich in meinem Befehl einen vollständigen Pfad zur Quelle, auch bekannt als use: "$ (pwd) / bull", anstelle des Dateinamens: bull? Die Sache ist, der Fehler ist, dass rsync das Zielverzeichnis erstellt, nicht die Quelle (bull). Mir scheint, rsync verwendet ein einfaches mkdir und kein mkdir -p.
Thebunnyrules
1
Übrigens ein großartiges Dienstprogramm, das ich möglicherweise in meinen Skripten verwende. Vielen Dank für die Empfehlung.
Thebunnyrules
1
rsync ist vorhersehbarer als cp. Wenn ich zum Beispiel möchte, cp /from/somedir /to-dirverhält sich cp anders, wenn /to-dires bereits existiert: Wenn es /to-direxistiert, cpwird erstellt /to-dir/some-dir, andernfalls wird nur der Inhalt von /from/somedirplatziert /to-dir. Verhält rsyncsich jedoch gleich für den Fall, /to-dir/some-dirdass immer erstellt wird.
JoeAC
5

Nur um fortzufahren und eine vollständige Arbeitslösung in einer Zeile zu geben. Seien Sie vorsichtig, wenn Sie Ihre Datei umbenennen möchten. Geben Sie eine Möglichkeit an, einen sauberen Verzeichnispfad zu mkdir bereitzustellen. $ fdst kann eine Datei oder ein Verzeichnis sein. Der nächste Code sollte auf jeden Fall funktionieren.

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p $(dirname ${fdst}) && cp -p ${fsrc} ${fdst}

oder bash spezifisch

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p ${fdst%/*} && cp -p ${fsrc} ${fdst}
Kaalahaan
quelle
Vielen Dank! Das brauchte ich. In meinem Fall weiß ich nicht, ob das Ziel ein Verzeichnis hat oder nicht und ob das Verzeichnis existiert oder nicht. Dieser Code gibt mir das übergeordnete Verzeichnis, das ich dann sicher erstellen kann, wenn es nicht existiert
xbmono
4

Fügen Sie einfach Folgendes in Ihre .bashrc ein und optimieren Sie es bei Bedarf. Funktioniert in Ubuntu.

mkcp() {
    test -d "$2" || mkdir -p "$2"
    cp -r "$1" "$2"
}

Beispiel: Wenn Sie die 'Test'-Datei in das Zielverzeichnis' d 'Use kopieren möchten,

mkcp test a/b/c/d

mkcp prüft zunächst, ob das Zielverzeichnis vorhanden ist oder nicht. Wenn nicht, erstellen Sie es und kopieren Sie die Quelldatei / das Quellverzeichnis.

SD.
quelle
Da andere die andere Antwort kommentierten, müssen Sie vor dem Aufruf nicht testen, ob das Verzeichnis vorhanden ist mkdir -p. Es wird erfolgreich sein, auch wenn es bereits existiert.
Bfontaine
3

Das macht es für mich

cp -vaR ./from ./to
Leroy Dunn
quelle
Zustimmen. In man cp:-R If source_file designates a directory, cp copies the directory and the entire subtree connected at that point.
BorracciaBlu
3

Ich habe ein Support-Skript für cp namens CP (Note Großbuchstaben) geschrieben, das genau dies tun soll. Das Skript prüft den von Ihnen eingegebenen Pfad auf Fehler (mit Ausnahme des letzten, der das Ziel ist). Wenn alles in Ordnung ist, führt es einen mkdir -p-Schritt aus, um den Zielpfad zu erstellen, bevor die Kopie gestartet wird. Zu diesem Zeitpunkt übernimmt das reguläre Dienstprogramm cp und alle Schalter, die Sie mit CP verwenden (wie -r, -p, -rpL werden direkt an cp weitergeleitet). Bevor Sie mein Skript verwenden, müssen Sie einige Dinge verstehen.

  • Auf alle Informationen hier kann mit CP --help zugegriffen werden. CP --help-all enthält die Schalter von cp.
  • reguläres cp kopiert nicht, wenn es den Zielpfad nicht findet. Sie haben kein solches Sicherheitsnetz für Tippfehler mit CP. Ihr Ziel wird erstellt. Wenn Sie also Ihr Ziel als / usrr / share / icons oder / usr / share / icon falsch schreiben, wird dies erstellt.
  • reguläres cp neigt dazu, sein Verhalten auf dem vorhandenen Pfad zu modellieren: cp / a / b / c / d hängt davon ab, ob d existiert oder nicht. Wenn d ein vorhandener Ordner ist, kopiert cp b in diesen Ordner und macht / c / d / b. Wenn d nicht existiert, wird b in c kopiert und in d umbenannt. Wenn d existiert, aber eine Datei ist und b eine Datei ist, wird es durch die Kopie von b überschrieben. Wenn c nicht existiert, kopiert cp nicht und wird beendet.

CP hat nicht den Luxus, sich an bestehenden Pfaden zu orientieren, daher muss es einige sehr feste Verhaltensmuster haben. CP geht davon aus, dass das zu kopierende Element im Zielpfad abgelegt wird und nicht das Ziel selbst ist (auch bekannt als umbenannte Kopie der Quelldatei / des Quellordners). Bedeutung:

  • "CP / a / b / c / d" führt zu / c / d / b, wenn d ein Ordner ist
  • "CP / a / b / c / b" führt zu / c / b / b, wenn b in / c / b ein Ordner ist.
  • Wenn sowohl b als auch d Dateien sind: CP / a / b / c / d führt zu / c / d (wobei d eine Kopie von b ist). Gleiches gilt für CP / a / b / c / b unter den gleichen Umständen.

Dieses Standard-CP-Verhalten kann mit dem Schalter "--rename" geändert werden. In diesem Fall wird davon ausgegangen, dass

  • "CP - Name / a / b / c / d" kopiert b in / c und benennt die Kopie in d um.

Einige abschließende Hinweise: Wie bei cp kann CP mehrere Elemente gleichzeitig kopieren, wobei der zuletzt aufgeführte Pfad als Ziel angenommen wird. Es kann auch Pfade mit Leerzeichen verarbeiten, solange Sie Anführungszeichen verwenden.

CP überprüft die von Ihnen eingegebenen Pfade und stellt sicher, dass sie vorhanden sind, bevor Sie die Kopie erstellen. Im strengen Modus (verfügbar über den Schalter --strict) müssen alle zu kopierenden Dateien / Ordner vorhanden sein, da sonst keine Kopie erfolgt. Im entspannten Modus (--relaxed) wird der Kopiervorgang fortgesetzt, wenn mindestens eines der von Ihnen aufgelisteten Elemente vorhanden ist. Der entspannte Modus ist die Standardeinstellung. Sie können den Modus vorübergehend über die Schalter oder dauerhaft ändern, indem Sie die Variable easy_going am Anfang des Skripts festlegen.

So installieren Sie es:

Führen Sie in einem Nicht-Root-Terminal Folgendes aus:

sudo echo > /usr/bin/CP; sudo chmod +x /usr/bin/CP; sudo touch /usr/bin/CP
gedit admin:///usr/bin/CP 

Fügen Sie in gedit das CP-Dienstprogramm ein und speichern Sie:

#!/bin/bash
#Regular cp works with the assumption that the destination path exists and if it doesn't, it will verify that it's parent directory does.

#eg: cp /a/b /c/d will give /c/d/b if folder path /c/d already exists but will give /c/d (where d is renamed copy of b) if /c/d doesn't exists but /c does.

#CP works differently, provided that d in /c/d isn't an existing file, it assumes that you're copying item into a folder path called /c/d and will create it if it doesn't exist. so CP /a/b /c/d will always give /c/d/b unless d is an existing file. If you put the --rename switch, it will assume that you're copying into /c and renaming the singl item you're copying from b to d at the destination. Again, if /c doesn't exist, it will be created. So CP --rename /a/b /c/d will give a /c/d and if there already a folder called /c/d, contents of b will be merged into d. 

#cp+ $source $destination
#mkdir -p /foo/bar && cp myfile "$_"

err=0 # error count
i=0 #item counter, doesn't include destination (starts at 1, ex. item1, item2 etc)
m=0 #cp switch counter (starts at 1, switch 1, switch2, etc)
n=1 # argument counter (aka the arguments inputed into script, those include both switches and items, aka: $1 $2 $3 $4 $5)
count_s=0
count_i=0
easy_going=true #determines how you deal with bad pathes in your copy, true will allow copy to continue provided one of the items being copied exists, false will exit script for one bad path. this setting can also be changed via the custom switches: --strict and --not-strict
verbal="-v"


  help="===============================================================================\
    \n         CREATIVE COPY SCRIPT (CP) -- written by thebunnyrules\
    \n===============================================================================\n
    \n This script (CP, note capital letters) is intended to supplement \
    \n your system's regular cp command (note uncapped letters). \n
    \n Script's function is to check if the destination path exists \
    \n before starting the copy. If it doesn't it will be created.\n    
    \n To make this happen, CP assumes that the item you're copying is \
    \n being dropped in the destination path and is not the destination\
    \n itself (aka, a renamed copy of the source file/folder). Meaning:\n 
    \n * \"CP /a/b /c/d\" will result in /c/d/b \
    \n * even if you write \"CP /a/b /c/b\", CP will create the path /a/b, \
    \n   resulting in /c/b/b. \n
    \n Of course, if /c/b or /c/d are existing files and /a/b is also a\
    \n file, the existing destination file will simply be overwritten. \
    \n This behavior can be changed with the \"--rename\" switch. In this\
    \n case, it's assumed that \"CP --rename /a/b /c/d\" is copying b into /c  \
    \n and renaming the copy to d.\n
    \n===============================================================================\
    \n        CP specific help: Switches and their Usages \
    \n===============================================================================\n
    \
    \n  --rename\tSee above. Ignored if copying more than one item. \n
    \n  --quiet\tCP is verbose by default. This quiets it.\n
    \n  --strict\tIf one+ of your files was not found, CP exits if\
    \n\t\tyou use --rename switch with multiple items, CP \
    \n\t\texits.\n
    \n  --relaxed\tIgnores bad paths unless they're all bad but warns\
    \n\t\tyou about them. Ignores in-appropriate rename switch\
    \n\t\twithout exiting. This is default behavior. You can \
    \n\t\tmake strict the default behavior by editing the \
    \n\t\tCP script and setting: \n
    \n\t\teasy_going=false.\n
    \n  --help-all\tShows help specific to cp (in addition to CP)."

cp_hlp="\n\nRegular cp command's switches will still work when using CP.\
    \nHere is the help out of the original cp command... \
    \n\n===============================================================================\
    \n          cp specific help: \
    \n===============================================================================\n"

outro1="\n******************************************************************************\
    \n******************************************************************************\
    \n******************************************************************************\
    \n        USE THIS SCRIPT WITH CARE, TYPOS WILL GIVE YOU PROBLEMS...\
    \n******************************************************************************\
    \n******************************* HIT q TO EXIT ********************************\
    \n******************************************************************************"


#count and classify arguments that were inputed into script, output help message if needed
while true; do
    eval input="\$$n"
    in_=${input::1}

    if [ -z "$input" -a $n = 1 ]; then input="--help"; fi 

    if [ "$input" = "-h" -o "$input" = "--help" -o "$input" = "-?" -o "$input" = "--help-all" ]; then
        if [ "$input" = "--help-all" ]; then 
            echo -e "$help"$cp_hlp > /tmp/cp.hlp 
            cp --help >> /tmp/cp.hlp
            echo -e "$outro1" >> /tmp/cp.hlp
            cat /tmp/cp.hlp|less
            cat /tmp/cp.hlp
            rm /tmp/cp.hlp
        else
            echo -e "$help" "$outro1"|less
            echo -e "$help" "$outro1"
        fi
        exit
    fi

    if [ -z "$input" ]; then
        count_i=$(expr $count_i - 1 ) # remember, last item is destination and it's not included in cound
        break 
    elif [ "$in_" = "-" ]; then
        count_s=$(expr $count_s + 1 )
    else
        count_i=$(expr $count_i + 1 )
    fi
    n=$(expr $n + 1)
done

#error condition: no items to copy or no destination
    if [ $count_i -lt 0 ]; then 
            echo "Error: You haven't listed any items for copying. Exiting." # you didn't put any items for copying
    elif [ $count_i -lt 1 ]; then
            echo "Error: Copying usually involves a destination. Exiting." # you put one item and no destination
    fi

#reset the counter and grab content of arguments, aka: switches and item paths
n=1
while true; do
        eval input="\$$n" #input=$1,$2,$3,etc...
        in_=${input::1} #first letter of $input

        if [ "$in_" = "-" ]; then
            if [ "$input" = "--rename" ]; then 
                rename=true #my custom switches
            elif [ "$input" = "--strict" ]; then 
                easy_going=false #exit script if even one of the non-destinations item is not found
            elif [ "$input" = "--relaxed" ]; then 
                easy_going=true #continue script if at least one of the non-destination items is found
            elif [ "$input" = "--quiet" ]; then 
                verbal=""
            else
                #m=$(expr $m + 1);eval switch$m="$input" #input is a switch, if it's not one of the above, assume it belongs to cp.
                switch_list="$switch_list \"$input\""
            fi                                  
        elif ! [ -z "$input" ]; then #if it's not a switch and input is not empty, it's a path
                i=$(expr $i + 1)
                if [ ! -f "$input" -a ! -d "$input" -a "$i" -le "$count_i" ]; then 
                    err=$(expr $err + 1 ); error_list="$error_list\npath does not exit: \"b\""
                else
                    if [ "$i" -le "$count_i" ]; then 
                        eval item$i="$input" 
                        item_list="$item_list \"$input\""
                    else
                        destination="$input" #destination is last items entered
                    fi
                fi
        else
            i=0
            m=0
            n=1                     
            break
        fi      
        n=$(expr $n + 1)
done

#error condition: some or all item(s) being copied don't exist. easy_going: continue if at least one item exists, warn about rest, not easy_going: exit.
#echo "err=$err count_i=$count_i"
if [ "$easy_going" != true -a $err -gt 0 -a $err != $count_i ]; then 
    echo "Some of the paths you entered are incorrect. Script is running in strict mode and will therefore exit."
    echo -e "Bad Paths: $err $error_list"
    exit
fi

if [ $err = $count_i ]; then
    echo "ALL THE PATHS you have entered are incorrect! Exiting."
    echo -e "Bad Paths: $err $error_list"
fi

#one item to one destination:
#------------------------------
#assumes that destination is folder, it does't exist, it will create it. (so copying /a/b/c/d/firefox to /e/f/firefox will result in /e/f/firefox/firefox
#if -rename switch is given, will assume that the top element of destination path is the new name for the the item being given.

#multi-item to single destination:
#------------------------------
#assumes destination is a folder, gives error if it exists and it's a file. -rename switch will be ignored.

#ERROR CONDITIONS: 
# - multiple items being sent to a destination and it's a file.
# - if -rename switch was given and multiple items are being copied, rename switch will be ignored (easy_going). if not easy_going, exit.
# - rename option but source is folder, destination is file, exit.
# - rename option but source is file and destination is folder. easy_going: option ignored.

if [ -f "$destination" ]; then
    if [ $count_i -gt 1 ]; then 
        echo "Error: You've selected a single file as a destination and are copying multiple items to it. Exiting."; exit
    elif [ -d "$item1" ]; then
        echo "Error: Your destination is a file but your source is a folder. Exiting."; exit
    fi
fi
if [ "$rename" = true ]; then
    if [ $count_i -gt 1 ]; then
        if [ $easy_going = true ]; then
            echo "Warning: you choose the rename option but are copying multiple items. Ignoring Rename option. Continuing."
        else
            echo "Error: you choose the rename option but are copying multiple items. Script running in strict mode. Exiting."; exit
        fi
    elif [ -d "$destination" -a -f "$item1" ]; then
        echo -n "Warning: you choose the rename option but source is a file and destination is a folder with the same name. "
        if [ $easy_going = true ]; then
            echo "Ignoring Rename option. Continuing."
        else
            echo "Script running in strict mode. Exiting."; exit
        fi
    else
        dest_jr=$(dirname "$destination")
        if [ -d "$destination" ]; then item_list="$item1/*";fi
        mkdir -p "$dest_jr"
    fi
else
    mkdir -p "$destination"
fi

eval cp $switch_list $verbal $item_list "$destination"

cp_err="$?"
if [ "$cp_err" != 0 ]; then 
    echo -e "Something went wrong with the copy operation. \nExit Status: $cp_err"
else 
    echo "Copy operation exited with no errors."
fi

exit
thebunnyrules
quelle
Dies ist vielleicht ein Overkill, aber ziemlich effizient, funktioniert wie erwartet, danke, guter Herr.
Xedret
2

cp hat mehrere Verwendungen:

$ cp --help
Usage: cp [OPTION]... [-T] SOURCE DEST
  or:  cp [OPTION]... SOURCE... DIRECTORY
  or:  cp [OPTION]... -t DIRECTORY SOURCE...
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.

@ AndyRoss Antwort funktioniert für die

cp SOURCE DEST

Stil cp, aber macht das Falsche, wenn Sie die verwenden

cp SOURCE... DIRECTORY/

Stil von cp.

Ich denke, dass "DEST" ohne einen abschließenden Schrägstrich in dieser Verwendung mehrdeutig ist (dh wo das Zielverzeichnis noch nicht existiert), weshalb möglicherweise cpnie eine Option dafür hinzugefügt wurde.

Hier ist meine Version dieser Funktion, die einen abschließenden Schrägstrich im Zielverzeichnis erzwingt:

cp-p() {
  last=${@: -1}

  if [[ $# -ge 2 && "$last" == */ ]] ; then
    # cp SOURCE... DEST/
    mkdir -p "$last" && cp "$@"
  else
    echo "cp-p: (copy, creating parent dirs)"
    echo "cp-p: Usage: cp-p SOURCE... DEST/"
  fi
}
Reich
quelle
2

Hatte gerade das gleiche Problem. Mein Ansatz war es, die Dateien einfach so in ein Archiv zu tarieren:

tar cf your_archive.tar file1 /path/to/file2 path/to/even/deeper/file3

tar speichert die Dateien automatisch in der entsprechenden Struktur im Archiv. Wenn du läufst

tar xf your_archive.tar

Die Dateien werden in die gewünschte Verzeichnisstruktur extrahiert.

Chris
quelle
1

Wie oben von help_asap und spongeman vorgeschlagen, können Sie mit dem Befehl 'install' Dateien in vorhandene Verzeichnisse kopieren oder neue Zielverzeichnisse erstellen, sofern diese noch nicht vorhanden sind.

Option 1 install -D filename some/deep/directory/filename
kopiert die Datei in ein neues oder vorhandenes Verzeichnis und erteilt dem Dateinamen standardmäßig 755 Berechtigungen

Option 2 install -D filename -m640 some/deep/directory/filename
gemäß Option 1, gibt jedoch Dateinamen 640-Berechtigungen.

Option 3 install -D filename -m640 -t some/deep/directory/
gemäß Option 2, zielt jedoch auf den Dateinamen im Zielverzeichnis ab, sodass der Dateiname nicht sowohl im Quell- als auch im Zielverzeichnis geschrieben werden muss.

Option 4 install -D filena* -m640 -t some/deep/directory/
gemäß Option 3, verwendet jedoch einen Platzhalter für mehrere Dateien.

Es funktioniert gut in Ubuntu und kombiniert zwei Schritte (Verzeichniserstellung, dann Dateikopie) in einem einzigen Schritt.

Graben
quelle
1

Dies ist sehr spät, aber es kann einem Anfänger irgendwo helfen. Wenn Sie Ordner automatisch erstellen müssen, sollte rsync Ihr bester Freund sein. rsync / path / to / sourcefile / path / to / tragetdir / thatdoestexist /

immanski njoro
quelle
0
rsync file /path/to/copy/file/to/is/very/deep/there

Dies könnte funktionieren, wenn Sie die richtige Art von haben rsync.

danuker
quelle
Das funktioniert bei mir nicht - ich habe "Keine solche Datei oder kein solches Verzeichnis" im Zielverzeichnis
Rich
0

Kopieren Sie von der Quelle in einen nicht vorhandenen Pfad

mkdir p /destination && cp r /source/ $_

HINWEIS: Dieser Befehl kopiert alle Dateien

cp –r zum Kopieren aller Ordner und ihres Inhalts

$_ Arbeit als Ziel, das im letzten Befehl erstellt wurde

Entwicklerprashant
quelle
0

Einfach

cp -a * /path/to/dst/

sollte den Trick machen.

Brad D.
quelle
Das tut es nicht. Das '/ path / to / dst /' muss vorher existieren.
Shital Shah
-1

Nehmen wir an, Sie machen so etwas

cp file1.txt A / B / C / D / file.txt

Dabei sind A / B / C / D Verzeichnisse, die noch nicht existieren

Eine mögliche Lösung ist wie folgt

DIR=$(dirname A/B/C/D/file.txt)
# DIR= "A/B/C/D"
mkdir -p $DIR
cp file1.txt A/B/C/D/file.txt

Ich hoffe, das hilft!

sdinesh94
quelle