Linux: copia e crea la directory di destinazione se non esiste


348

Voglio un comando (o probabilmente un'opzione per cp) che crei la directory di destinazione se non esiste.

Esempio:

cp -? file /path/to/copy/file/to/is/very/deep/there


5
La risposta accettata è errata (esiste una tale opzione per cp). Vedi la seconda risposta di Paul Whipp.
Ronenz,

1
@Ronenz, sono d'accordo che esiste un'opzione degna di nota in GNU cp(come ha risposto Paul Whipp), ma non è così generale come richiesto dal PO. E 'possibile copiare a/foo/bar/filea b/foo/bar/filema non può copiare filea b/foo/bar/file. (vale a dire che funziona solo per creare directory padre già esistenti nel caso del primo file, ma non può creare directory arbitrarie)
Sudo Bash

Sembra una bella richiesta di funzionalità per cp. Qualche possibilità che potremmo ottenere un tale interruttore da riga di comando nel prossimo futuro?
Arnaud,

Risposte:


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

(non esiste tale opzione per cp).


62
Non penso che tu abbia bisogno di test -d: mkdir -priesce ancora anche se la directory esiste già.
effimero

oops, giusto. Ma poi, test potrebbe essere un built-in bash (specialmente se scritto come [[-d "$ d"]]) e mkdir non può ;-)
Michael Krelin - hacker

1
mkdirè incorporato in BusyBox;)
effimero

7
@holms, $dè una variabile che presumibilmente contiene la directory in questione. Voglio dire, se devi ripeterlo tre volte è probabile che tu lo assegni prima alla variabile.
Michael Krelin - hacker

237

Se sono vere entrambe le seguenti condizioni:

  1. Stai utilizzando la versione GNU di cp (e non, ad esempio, la versione Mac) e
  2. Stai copiando da una struttura di directory esistente e devi solo ricrearla

allora puoi farlo con la --parentsbandiera di cp. Dalla pagina delle informazioni (visualizzabile su http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html#cp-invocation o con info cpo 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.

Esempio:

/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

4
Questo non funziona su Mac OS X, quindi immagino sia specifico per Linux.
olt,

7
Direi specifico per lo gnu. Se hai macportspuoi installare coreutils e i suoi gcp.
Michael Krelin - hacker

16
Amico, Linux ha tutto
Ziggy il

19
Sento che questa è la risposta a una domanda leggermente diversa, anche se è la risposta che stavo cercando (e quindi quella che ho votato). Immagino che questa sia la soluzione presumendo che le directory principali di destinazione siano le stesse delle directory padre di origine, che è probabilmente il caso d'uso in cui la maggior parte delle persone che leggono questo è interessante.
David Winiecki,

7
Ciò presuppone che la "barra" della directory esista già, ma la domanda originale implica come creare automaticamente la "barra" quando viene fornita come destinazione e non esiste già. La risposta è fornita da @ MichaelKrelin-hacker.
thdoan

70

Risposta breve

Per copiare myfile.txta /foo/bar/myfile.txt, uso:

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

Come funziona?

Ci sono alcuni componenti per questo, quindi tratterò tutta la sintassi passo dopo passo.

L' utilità mkdir , come specificato nello standard POSIX , crea directory. L' -pargomento, per la documentazione, causerà mkdir per

Creare eventuali componenti intermedi del percorso mancanti

nel senso che quando si chiama mkdir -p /foo/bar, mkdir creerà /foo e /foo/bar se /foonon esiste già. (Senza-p , genererà invece un errore.

L' &&operatore dell'elenco, come documentato nello standard POSIX (o nel manuale di Bash, se preferisci), ha l'effetto che cp myfile.txt $_viene eseguito solo se mkdir -p /foo/bareseguito correttamente. Ciò significa che il cpcomando non tenterà di eseguire se mkdirfallisce per uno dei tanti motivi per cui potrebbe non riuscire .

Infine, $_passiamo come secondo argomento a cpun "parametro speciale" che può essere utile per evitare di ripetere argomenti lunghi (come i percorsi dei file) senza doverli archiviare in una variabile. Secondo il manuale di Bash , esso:

si espande all'ultimo argomento al comando precedente

In questo caso, è quello a cui /foo/barsiamo passati mkdir. Quindi il cpcomando si espande in cp myfile.txt /foo/bar, che copia myfile.txtnella nuova creazione/foo/bar directory .

Si noti che $_è non parte dello standard POSIX , quindi teoricamente una variante di Unix può avere un guscio che non supporta questo costrutto. Tuttavia, non conosco shell moderne che non supportano $_; sicuramente Bash, Dash e zsh lo fanno tutti.


Un'ultima nota: il comando che ho dato all'inizio di questa risposta presuppone che i nomi delle tue directory non abbiano spazi. Se hai a che fare con nomi con spazi, devi citarli in modo che le diverse parole non sono trattati come argomenti diversi da mkdiro cp. Quindi il tuo comando sembrerebbe davvero:

mkdir -p "/my directory/name with/spaces" && cp "my filename with spaces.txt" "$_"

17
A parte: di solito sono deluso dalla mancanza di dettagli nelle domande sui comandi della shell Bash o Unix su Stack Overflow, che spesso lanciano un one-liner complicato ed estremamente denso che è difficile da rimuovere se non sei già un mago Unix. Ho cercato di andare all'estremo opposto qui ed entrambi spiegano ogni dettaglio della sintassi e si collegano ai luoghi appropriati per trovare la documentazione su come funziona questa roba. Spero che questo aiuti almeno alcune persone. Inoltre, il credito è dovuto: ho tolto questo approccio da questa risposta a una domanda duplicata: stackoverflow.com/a/14085147/1709587
Mark Amery,

Non ho mai visto $ _ prima. a cosa si riferisce? la cartella utilizzata nell'ultimo comando?
thebunnyrules,

10
@thebunnyrules Ma ... ma ... c'è un "Come funziona?" sezione della mia risposta che contiene letteralmente più paragrafi specificamente dedicati alla domanda che hai appena posto. Sembra ignorato e non amato. :(
Mark Amery,

Mi dispiace per quello. Hai assolutamente ragione. Conoscevo già tutto il resto della tua risposta, quindi l'ho scannerizzata rapidamente. Avrei dovuto leggerlo più attentamente.
thebunnyrules,

Caspita, hai davvero dedicato molto lavoro a quella risposta ... Grazie per quello. È stato bello conoscere: $ _. Mi vedo usarlo un bel po 'da ora in poi. Ho scritto una sceneggiatura per automatizzare l'intero processo e inserirla in questo thread. Dategli un andare, forse ti piacerà: stackoverflow.com/questions/1529946/...
thebunnyrules

39

Una domanda così vecchia, ma forse posso proporre una soluzione alternativa.

È possibile utilizzare il installprogramma per copiare il file e creare il percorso di destinazione "al volo".

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

Vi sono tuttavia alcuni aspetti da tenere in considerazione:

  1. è necessario specificare anche il nome del file di destinazione , non solo il percorso di destinazione
  2. il file di destinazione sarà eseguibile (almeno, per quanto ho visto dai miei test)

Puoi facilmente modificare il n. 2 aggiungendo l' -mopzione per impostare le autorizzazioni sul file di destinazione (esempio: -m 664creerà il file di destinazione con autorizzazioni rw-rw-r--, proprio come creare un nuovo file con touch).


Ed ecco il link spudorato alla risposta da cui sono stato ispirato =)


Non ha funzionato davvero per il mio utilizzo ma un bel trucco! Lo terrò a mente.
thebunnyrules,

si noti che questo comando crea i file di destinazione con autorizzazioni 755( rwxr-xr-x), che probabilmente non è desiderato. è possibile specificare qualcos'altro con l' -minterruttore, ma non sono riuscito a trovare un modo per mantenere solo i permessi dei file :(
törzsmókus

19

Funzione shell che fa quello che vuoi, chiamandolo una copia "seppellire" perché scava una buca per il file in cui vivere:

bury_copy() { mkdir -p `dirname $2` && cp "$1" "$2"; }

1
Si dovrebbe avere le virgolette attorno alla dirname $2troppo
Tarnay Kálmán

4
@ Kalmi, per una corretta citazione che vorresti citare anche $2come argomento dirname. Come mkdir -p "$(dirname "$2")". Senza questo citare $2in cpè inutile;)
Michael Krelin - pirata

3
Si noti che questa risposta presuppone che $ 2 non sia già la directory di destinazione, altrimenti si vorrebbe semplicemente "mkdir -p" $ 2 "&&p" $ 1 "" $ 2 "come corpo della funzione
user467257

Penso che questo abbia un bug, come sottolinea @ user467257. Non penso che questo sia risolvibile, poiché $ 2 è ambiguo tra la directory di destinazione e il file di destinazione in questo scenario. Ho pubblicato una risposta alternativa che si rivolge a questo.
Ricco

@Rich, non sono d'accordo sul fatto che ciò non sia risolvibile. Quanto segue funziona bene sia per i file che per le directory: mkdir -p dirname "$2"&& cp -r "$ 1" "$ 2"; Si noti che i backtick in giro dirname $2non vengono visualizzati perché SO li analizza come marcatori di codice.
Yury,

11

Ecco un modo per farlo:

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

dirnameti darà il genitore della directory o del file di destinazione. mkdir -p `dirname ...` creerà quindi quella directory assicurandosi che quando chiami cp -r sia presente la directory base corretta.

Il vantaggio di questo over - genitori è che funziona nel caso in cui l'ultimo elemento nel percorso di destinazione sia un nome file.

E funzionerà su OS X.


6

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


1
in realtà, non è affatto lo stesso. suo richiede di passare due volte il nome del file (da qui il flag '-t') e imposta i bit di esecuzione sul file (da qui l'esempio '-m'). la sua non dovrebbe essere la risposta migliore, dal momento che in realtà non risponde alla domanda, mentre la mia lo fa.
Spongman,

1
Questo funziona per un singolo file. Basta prendere in considerazione che thereè il file finale e non la sottodirectory finale.
kvantour,

6

con tutto il rispetto per le risposte sopra, preferisco usare rsync come segue:

$  rsync -a directory_name /path_where_to_inject_your_directory/

esempio:

$ rsync -a test /usr/local/lib/

L'ho provato e ho ottenuto questo risultato: rsync -a bull / some / hoop / ti / doop / ploop RISULTATO rsync: mkdir "/ some / hoop / ti / doop / ploop" non riuscito: nessun errore rsync di file o directory (2) : errore nel file IO (codice 11) su main.c (675) [Ricevitore = 3.1.2]
thebunnyrules

@thebunnyrules devi controllare il percorso con il comando pwd (guarda: [link] ( access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/… )) e inseriscilo correttamente nelle tue istruzioni
marcdahan

Se capisco correttamente il tuo suggerimento, stai proponendo di specificare un percorso completo per la sorgente, ovvero uso: "$ (pwd) / bull" nel mio comando anziché solo il nome del file: bull? Il fatto è che l'errore è con rsync che crea la directory di destinazione, non l'origine (toro). Mi sembra che rsync usi un mkdir semplice e non un mkdir -p.
thebunnyrules

1
Grande utilità tra l'altro, potrei iniziare a usare nei miei script. Grazie per averlo raccomandato.
thebunnyrules

1
rsync è più prevedibile di cp. Ad esempio, se desidero cp /from/somedir /to-dirquindi cp si comporta diversamente se /to-diresiste già: se /to-diresiste, allora cpcreerà /to-dir/some-dir, altrimenti /from/somedirverranno inseriti solo i contenuti di /to-dir. Tuttavia, rsyncsi comporta allo stesso modo nel caso, ovvero, /to-dir/some-dirverrà sempre creato.
Joe CA

5

Solo per riprendere e dare una soluzione di lavoro completa, in una riga. Fai attenzione se vuoi rinominare il tuo file, dovresti includere un modo per fornire un percorso dir diretto a mkdir. $ fdst può essere file o dir. Il prossimo codice dovrebbe funzionare in ogni caso.

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

o bash specifico

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

Grazie! Questo è quello di cui avevo bisogno. Nel mio caso non so se la destinazione ha una directory o meno e se la directory esiste o meno quindi questo codice mi dà la directory principale che quindi posso crearla in modo sicuro se non esiste
xbmono

4

Aggiungi semplicemente quanto segue nel tuo .bashrc, modifica se necessario. Funziona in Ubuntu.

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

Ad esempio, se si desidera copiare il file "test" nella directory di destinazione "d" Usa,

mkcp test a/b/c/d

mkcp controllerà prima se esiste o meno la directory di destinazione, altrimenti crealo e copia il file / directory di origine.


Come altri hanno commentato l'altra risposta, non è necessario verificare se la directory esiste prima di chiamare mkdir -p. Ci riuscirà anche se esiste già.
bfontaine,

3

Questo lo fa per me

cp -vaR ./from ./to

Essere d'accordo. In man cp:-R If source_file designates a directory, cp copies the directory and the entire subtree connected at that point.
borracciaBlu,

3

Ho scritto uno script di supporto per cp, chiamato CP (nota maiuscola) che ha lo scopo di fare esattamente questo. Lo script verificherà la presenza di errori nel percorso inserito (tranne l'ultimo che è la destinazione) e, se tutto va bene, eseguirà un passaggio mkdir -p per creare il percorso di destinazione prima di avviare la copia. A questo punto subentra l'utility cp normale e tutti gli switch che usi con CP (come -r, -p, -rpL vengono reindirizzati direttamente a cp). Prima di usare la mia sceneggiatura, ci sono alcune cose che devi capire.

  • tutte le informazioni qui sono accessibili facendo CP - help. CP --help-all include gli switch cp di.
  • cp normale non eseguirà la copia se non trova il percorso di destinazione. Non hai una tale rete di sicurezza per errori di battitura con CP. La tua destinazione verrà creata, quindi se scrivi la tua destinazione come / usrr / share / icons o / usr / share / icon bene, questo è ciò che verrà creato.
  • cp regolare tende a modellare il suo comportamento sul percorso esistente: cp / a / b / c / d varierà a seconda che esista o meno. se d è una cartella esistente, cp copierà b in essa, creando / c / d / b. Se d non esiste, b verrà copiato in c e rinominato in d. Se d esiste ma è un file eb è un file, verrà sovrascritto dalla copia di b. Se c non esiste, cp non esegue la copia ed esce.

CP non ha il lusso di prendere spunti da percorsi esistenti, quindi deve avere alcuni modelli di comportamento molto fermi. CP presume che l'elemento che stai copiando venga eliminato nel percorso di destinazione e non sia la destinazione stessa (ovvero una copia rinominata del file / cartella di origine). Senso:

  • "CP / a / b / c / d" si tradurrà in / c / d / b se d è una cartella
  • "CP / a / b / c / b" risulterà in / c / b / b se b in / c / b è una cartella.
  • Se sia b che d sono file: CP / a / b / c / d comporterà / c / d (dove d è una copia di b). Lo stesso vale per CP / a / b / c / b nella stessa circostanza.

Questo comportamento CP predefinito può essere modificato con l'opzione "--rename". In questo caso, si presume che

  • "CP --rename / a / b / c / d" sta copiando b in / c e rinominando la copia in d.

Alcune note di chiusura: Come con cp, CP può copiare più elementi contemporaneamente con l'ultimo percorso elencato assunto come destinazione. Può anche gestire percorsi con spazi purché vengano utilizzate le virgolette.

CP verificherà i percorsi inseriti e si assicurerà che esistano prima di eseguire la copia. In modalità rigorosa (disponibile tramite l'opzione --strict), tutti i file / cartelle da copiare devono esistere o non viene eseguita alcuna copia. In modalità rilassata (--relaxed), la copia continuerà se esiste almeno uno degli elementi elencati. La modalità rilassata è l'impostazione predefinita, è possibile modificare la modalità temporaneamente tramite gli switch o in modo permanente impostando la variabile easy_going all'inizio dello script.

Ecco come installarlo:

In un terminale non root, eseguire:

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

In gedit, incolla l'utilità CP e salva:

#!/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

Questo è forse un problema eccessivo, ma piuttosto efficiente, funziona come previsto, grazie bravo signore.
Xedret,

2

cp ha più usi:

$ 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.

La risposta di @ AndyRoss funziona per

cp SOURCE DEST

stile di cp, ma fa la cosa sbagliata se si utilizza il

cp SOURCE... DIRECTORY/

stile di cp .

Penso che "DEST" sia ambiguo senza una barra finale in questo utilizzo (ovvero dove la directory di destinazione non esiste ancora), forse per questo cp non ha mai aggiunto un'opzione per questo.

Quindi, ecco la mia versione di questa funzione che impone una barra finale sulla directory dest:

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
}

2

Ho appena avuto lo stesso problema. Il mio approccio era semplicemente quello di tarare i file in un archivio in questo modo:

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

tar archivia automaticamente i file nella struttura appropriata all'interno dell'archivio. Se corri

tar xf your_archive.tar

i file vengono estratti nella struttura di directory desiderata.


1

Come suggerito sopra da help_asap e spongeman è possibile utilizzare il comando 'install' per copiare i file in directory esistenti o creare creare nuove directory di destinazione se non esistono già.

L'opzione 1 install -D filename some/deep/directory/filename
copia il file in una directory nuova o esistente e fornisce autorizzazioni 755 predefinite per il nome file

Opzione 2 install -D filename -m640 some/deep/directory/filename
come da opzione 1 ma dà le autorizzazioni per il nome file 640.

Opzione 3 install -D filename -m640 -t some/deep/directory/
come da opzione 2 ma indirizza il nome file nella directory di destinazione, quindi il nome file non deve essere scritto sia in origine che in destinazione.

Opzione 4 install -D filena* -m640 -t some/deep/directory/
come da opzione 3 ma utilizza un carattere jolly per più file.

Funziona bene in Ubuntu e combina due passaggi (creazione directory quindi copia file) in un unico passaggio.


1

È molto tardi, ma può aiutare un novellino da qualche parte. Se devi "auto" creare cartelle, rsync dovrebbe essere il tuo migliore amico. rsync / path / to / sourcefile / path / to / tragetdir / thatdoestexist /


0
rsync file /path/to/copy/file/to/is/very/deep/there

Questo potrebbe funzionare, se hai il giusto tipo di rsync.


Questo non funziona per me - Ho ricevuto "Nessun file o directory" sulla directory di destinazione
Rich

0

Copia dall'origine in un percorso inesistente

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

NOTA: questo comando copia tutti i file

cp –r per copiare tutte le cartelle e il suo contenuto

$_ funziona come destinazione creata nell'ultimo comando


0

Semplice

cp -a * /path/to/dst/

dovrebbe fare il trucco.


Non '/ Path / to / dst /' deve esistere prima.
Shital Shah,

-1

Diciamo che stai facendo qualcosa del genere

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

dove A / B / C / D sono directory che non esistono ancora

Una possibile soluzione è la seguente

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

spero che aiuti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.