Unire le cartelle con mv?


149

Se uso mvper spostare una cartella denominata "cartella" in una directory che contiene già "cartella", si uniranno o verranno sostituiti?

Risposte:


120

mvimpossibile unire o sovrascrivere le directory, fallirà con il messaggio "mv: impossibile spostare" a "in" b ": directory non vuota" , anche quando si utilizza l' --forceopzione.


È possibile aggirare questo utilizzando altri strumenti (come rsync, findo anche cp), ma è necessario considerare con attenzione le implicazioni:

  • rsyncpuò unire i contenuti di una directory in un'altra (idealmente con l' opzione --remove-source-files1 per eliminare in modo sicuro solo i file di origine che sono stati trasferiti correttamente e con la consueta opzione di autorizzazione / proprietà / conservazione del tempo -ase lo si desidera)
    ... ma questa è un'operazione di copia completa e può quindi essere molto intensivo per il disco.
  • Opzione attualmente preferita: è possibile combinare rsyncl' --link-dest=DIRopzione (per creare collegamenti fissi invece di copiare il contenuto del file, ove possibile) e --remove-source-filesottenere una semantica molto simile a una normale mv.
    Per questo, --link-destdeve essere assegnato un percorso assoluto alla directory di origine (o un percorso relativo dalla destinazione alla fonte ).
    ... ma questo sta usando --link-destin modo non intenzionale (che può o meno causare complicazioni), richiede di conoscere (o determinare) il percorso assoluto verso la fonte (come argomento per --link-dest), e di nuovo lascia una struttura di directory vuota da ripulire come per 1 .
  • È possibile utilizzarefind per ricreare in sequenza la struttura della directory di origine sulla destinazione, quindi spostare individualmente i file effettivi
    ... ma questo deve ricorrere attraverso la fonte più volte e può incontrare condizioni di competizione (nuove directory vengono create alla fonte durante il processo in più passaggi )
  • cppuò creare collegamenti fissi (semplicemente, puntatori aggiuntivi allo stesso file esistente), che crea un risultato molto simile a una fusione mv(ed è molto efficiente in termini di IO poiché vengono creati solo puntatori e non è necessario copiare dati effettivi)
    ... ma questo soffre di nuovo di una possibile condizione di competizione (i nuovi file alla fonte vengono eliminati anche se non sono stati copiati nel passaggio precedente)

Quale di queste soluzioni alternative (se presenti) è appropriata dipenderà molto dal tuo caso d'uso specifico.
Come sempre, pensa prima di eseguire uno di questi comandi e disponi di backup.


1: Nota che rsync --remove-source-filesnon cancellerai nessuna directory, quindi dovrai fare qualcosa come find -depth -type d -empty -deletedopo per sbarazzarti della struttura della directory dei sorgenti vuota.


Sembra che tu abbia provato una sola implementazione di mv. Questa risposta sarebbe migliore con una verità più ampia. Linux, BSD e Unix "reale", o un riferimento da POSIX o SUS.
Warren Young,

@WarrenYoung Hai ragione, ho solo provato l' mvimplementazione usata da Debian - l'enfasi è stata provata , dato che la manpage non menziona questo comportamento ...
n.

38
Lo svantaggio di rsync è che in realtà copia i dati, piuttosto che cambiare semplicemente il collegamento reale, che è potenzialmente ad alta intensità di risorse se si ha a che fare con molti dati.
Jonathan Mayer,

7
@Keith Nota che --deleteelimina solo i file nella directory di destinazione che non esistono nella directory di origine.
n.

1
@JonathanMayer rsync come diverse funzionalità correlate al collegamento reale. Ad esempio puoi semplicemente conservare i collegamenti -Hfisici con la funzione o puoi collegare i file nella destinazione usando --link-dest. Vedi la pagina man prima di usarli, però.
allo

87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/

Questo rimuoverà i file sorgente come nel commento di n.st?
Dominique,

3
No, preferirei farlo in due passaggi per motivi di sicurezza. La fonte unita e rimossa non è reversibile. È inoltre necessario un passaggio aggiuntivo in n.st anwer (per rimuovere le directory).
fazie,

3
--remove-source-filesha il vantaggio di rimuovere solo i file che sono stati trasferiti correttamente, quindi è possibile utilizzare findper rimuovere directory vuote e verrà lasciato con tutto ciò che non è stato trasferito senza dover controllare l' rsyncoutput.
n.

4
Ma in realtà non si sta muovendo: l'impatto della velocità è enorme se sono coinvolti file di grandi dimensioni.
Alex

Ma in realtà non puoi eseguire mosse e fusioni pure.
fazie il

64

È possibile utilizzare l' -lopzione del comando cp , che crea collegamenti reali di file sullo stesso filesystem anziché copie complete. Il comando seguente copia la cartella source/folderin una cartella principale ( destination) che contiene già una directory con il nome folder.

cp -rl source/folder destination
rm -r source/folder

Puoi anche utilizzare il -P( --no-dereference- non rimandare i collegamenti simbolici) o -a( --archive- conservare tutti i metadati, include anche l' -Popzione), a seconda delle tue esigenze.


7
@rautamiekka: suppongo che tu stia chiedendo il motivo per utilizzare i collegamenti reali. Se non sai quali sono gli hard link e perché dovresti usarli, probabilmente non dovresti seguire questa strada. Tuttavia, la creazione di collegamenti reali non esegue una copia completa, quindi questa operazione richiederebbe ordini di grandezza meno tempo rispetto a una copia completa. Inoltre, utilizzeresti hard link anziché soft link in modo da poter eliminare i file di origine e avere comunque i dati corretti anziché i puntatori a percorsi non validi. E cppiuttosto che da rsyncquando ogni sistema ha cpe tutti hanno familiarità con esso.
Palswim,

7
La brillantezza di questa soluzione può essere considerata come non la risposta accettata. È una soluzione elegante. Ottieni la capacità di unione di cpcon il tempo di operazione di mv.
TheHerk

2
se sai che non hai bisogno di spostare i file che già esistono sulla destinazione, vuoi anche aggiungere-n
ndemou,

1
@Ruslan: questo è vero, ma non puoi spostarti senza una copia tra i filesystem con qualsiasi metodo. Anche mv /fs1/file /fs2/(attraverso i filesystem) eseguirà una copia e quindi una cancellazione.
Palswim,

2
Giusto, ma mentre mvfunzionerà (a condizione che la directory di destinazione non esista ancora) anche se non "in modo efficiente" o come la chiami, cp -rlfallirà.
Ruslan,

23

Consiglierei questi quattro passaggi:

cd ${SOURCE}; 
find . -type d -exec mkdir -p ${DEST}/\{} \; 
find . -type f -exec mv \{} ${DEST}/\{} \; 
find . -type d -empty -delete

o meglio ancora, ecco uno script che implementa la semantica simile a mv:

#!/bin/bash

DEST="${@:${#@}}"
ABS_DEST="$(cd "$(dirname "$DEST")"; pwd)/$(basename "$DEST")"

for SRC in ${@:1:$((${#@} -1))}; do   (
    cd "$SRC";
    find . -type d -exec mkdir -p "${ABS_DEST}"/\{} \;
    find . -type f -exec mv \{} "${ABS_DEST}"/\{} \;
    find . -type d -empty -delete
) done

Args sono SOURCE, DEST
schuess

Questo sembra abbastanza utile. Sono tentato di usarlo per ripulire il mio disco rigido. Qualche altro esperto può commentarlo prima di affidare un sacco di backup a questo script? :-)
LarsH

A proposito, se volevi fare l'equivalente di rsync -u(aggiorna solo se più recente), mv(almeno in alcune versioni) puoi anche prendere l' -uopzione. Tuttavia, in tal caso, potresti voler eliminare le directory dei sorgenti non vuote e anche quelle vuote, per coprire i casi in cui i file nella struttura dei sorgenti non sono più recenti. @schuess: sembra che ci possano essere più argomenti SOURCE, se ne hai bisogno.
LarsH,

1
Questo non gestisce bene gli spazi bianchi. Ho provato con alcune directory con spazi in esse e ho finito con una serie infinita di directory nidificate.
rofer

16

Ecco un modo che unirà le directory. È molto più veloce di rsync dal momento che rinomina i file invece di copiarli e quindi eliminarli.

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'

Questo è interessante ma solo vagamente rilevante per l'argomento e nemmeno lontanamente ciò che l'utente ha chiesto.
Shadur,

17
In realtà, il codice Jewel fa esattamente ciò che l'utente ha richiesto, ad eccezione della creazione di directory mancanti. Forse dovresti guardare di nuovo?
Jonathan Mayer,

3
Aggiungerei per usare "-print0" in find e "-0" in xargs perché ci sono file con spazi nei nomi. Inoltre, c'è un piccolo problema, se un nome contiene parentesi non verranno spostate.
markuz,

2
Questo è molto più veloce di rsync per un piccolo numero di file, ma crea un nuovo processo per ogni file, quindi le prestazioni sono spaventose con un gran numero di piccoli file. La risposta di @ palswim non soffre di questo problema.
b0fh

2
Il comando fallirà, se in destè già una directory con lo stesso nome di in source. E i file verranno spostati in a dest, che è in source. Il comando non fa altro chemv source/* source/dest/.
seppellire il

3

Un modo per ottenere ciò sarebbe usare:

mv folder/* directory/folder/
rmdir folder

Finché non ci sono due file con lo stesso nome foldere directory/folder, otterrai lo stesso risultato, ovvero l'unione.


3
Come funziona esattamente rm folder?
Jake Gould

5
@JakeGould Niente affatto. :)
n.

rm folder -fRfunziona sempre per me
Octopus il

2

2

Per le copie più pure, uso il metodo di copia blockread tar (-) B.

esempio, dal percorso sorgente ('cd' lì se necessario):

tar cBf - <sourcefolder> | (cd /your/target/folder ; tar xBf -)

questo crea una copia esatta dell'albero dei sorgenti, con il proprietario e le autorizzazioni intatte. E se esiste la cartella di destinazione, i dati verranno uniti. Solo i file già esistenti verranno sovrascritti.

Esempio:

 $ cd /data1/home
 $ tar cBf - jdoe | (cd /data2/home ; tar xBf -)

Quando l'azione di copia ha esito positivo, è possibile rimuovere l'origine ( rm -rf <source>). Naturalmente questa non è una mossa esatta: i dati verranno copiati, fino a quando non rimuovi la fonte.

Come opzione puoi essere prolisso (visualizza sullo schermo il file da copiare), con -v: tar cBvf -

  • c: creare
  • B: leggi blocco completo (per pipe letto)
  • v: verboso
  • f: file da scrivere
  • x: estratto
  • -: stdout / stdin

sourcefolderpuò anche essere *(per qualsiasi cosa nella cartella corrente)


La specifica f -di tar non è in genere necessaria: l'impostazione predefinita è leggere da stdin / scrivere a stdout.
Muru,

1

Ecco una sceneggiatura che ha funzionato per me. Preferisco mv a rsync, quindi uso le soluzioni di Jewel e Jonathan Mayer.

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd $SRC;
    find . -type d -exec mkdir -p ${DEST}/{} \;
    find . -type f -exec mv {} ${DEST}/{} \;
    find . -type d -empty -delete
    popd
done

Questa soluzione non sfugge correttamente ai nomi dei percorsi, fare attenzione.
user12439

@ user12439, aggiornerò la soluzione se mi mostri quale parte correggere.
xer0x,

1

Non è una buona idea usare comandi come cp o rsync. Per file di grandi dimensioni, ci vorrà molto tempo. mv è molto più veloce poiché aggiorna solo gli inode senza copiare fisicamente i file. Un'opzione migliore è usare il file manager del tuo sistema operativo. Per Opensuse, esiste un file manager chiamato Konquerer. Può spostare i file senza copiarli. Ha la funzione "taglia e incolla" come in Windows. Basta selezionare tutte le sottodirectory nella directory A. Fare clic con il tasto destro del mouse e "spostarsi" nella directory B che può contenere sottodirectory con gli stessi nomi. Li unirà. Esistono anche opzioni per sovrascrivere o rinominare i file con lo stesso nome.


1
OP chiede cosa succede quando mvviene utilizzato.
don_crissti,

0

Soluzione Python

Dato che non riuscivo a trovare una soluzione preesistente soddisfacente, ho deciso di creare un rapido script Python per raggiungerlo.

In particolare, questo metodo è efficace perché percorre l'albero dei file di origine solo una volta dal basso verso l'alto.

Ti permetterà anche di modificare rapidamente cose come la gestione della sovrascrittura dei file a tuo piacimento.

Uso:

move-merge-dirs src/ dest/

sposterà tutti i contenuti di src/*in dest/e src/scomparirà.

move-unione-dirs

#!/usr/bin/env python3

import argparse
import os

def move_merge_dirs(source_root, dest_root):
    for path, dirs, files in os.walk(source_root, topdown=False):
        dest_dir = os.path.join(
            dest_root,
            os.path.relpath(path, source_root)
        )
        if not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        for filename in files:
            os.rename(
                os.path.join(path, filename),
                os.path.join(dest_dir, filename)
            )
        for dirname in dirs:
            os.rmdir(os.path.join(path, dirname))
    os.rmdir(source_root)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Move merge src/* into dest. Overwrite existing files.'
    )
    parser.add_argument('src_dir')
    parser.add_argument('dest_dir')
    args = parser.parse_args()
    move_merge_dirs(args.src_dir, args.dest_dir)

GitHub a monte .

Vedi anche: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command


0

Questo è il comando per spostare file e cartelle in un'altra destinazione:

$ mv /source/path/folder /target/destination/

Ricorda : il mvcomando non funzionerà se la cartella è b̲e̲i̲n̲g m̲e̲r̲ge̲d̲ (cioè un'altra cartella con lo stesso nome esiste già nella destinazione) e il d̲e̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲pt̲y .

mv: impossibile spostare "/ sorgente / percorso / cartella" su "/ destinazione / destinazione / cartella": directory non vuota

Se la cartella di destinazione è vuota, il comando sopra funzionerà correttamente.

Quindi, per unire entrambe le cartelle in ogni caso,
fallo in 2 comandi:

$ cp -rf /source/path/folder /target/destination/
$ rm -rf /source/path/folder

Oppure combina entrambi come comando singolo:

$ cp -rf /source/path/folder /target/destination/ && rm -rf /source/path/folder

mv = sposta
cp = copia
rm = rimuovi

-r per directory (cartella)
-f forza l'esecuzione

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.