Menggabungkan folder dengan mv?

149

Jika saya menggunakan mvuntuk memindahkan folder yang disebut "folder" ke direktori yang sudah berisi "folder" apakah mereka akan bergabung atau akan diganti?

Dominique
sumber

Jawaban:

120

mvtidak dapat menggabungkan atau menimpa direktori, itu akan gagal dengan pesan "mv: tidak dapat memindahkan 'a' ke 'b': Direktori tidak kosong" , bahkan ketika Anda menggunakan --forceopsi.


Anda bisa menyiasatinya dengan menggunakan alat-alat lain (seperti rsync, find, atau bahkan cp), tetapi Anda perlu hati-hati mempertimbangkan implikasi:

  • rsyncdapat menggabungkan konten dari satu direktori ke direktori lain (idealnya dengan opsi --remove-source-files1 untuk menghapus dengan aman hanya file-file sumber yang berhasil ditransfer, dan dengan opsi pelestarian izin / kepemilikan / waktu yang biasa -ajika Anda inginkan)
    ... tetapi ini adalah operasi salinan lengkap , dan karena itu bisa sangat intensif disk.
  • Pilihan saat ini disukai: Anda dapat menggabungkan rsync's --link-dest=DIRpilihan (untuk membuat hardlinks bukan menyalin isi file, jika mungkin) dan --remove-source-filesuntuk mendapatkan semantik sangat mirip dengan biasa mv.
    Untuk ini, --link-destperlu diberikan jalur absolut ke direktori sumber (atau jalur relatif dari tujuan ke sumber ).
    ... tetapi ini menggunakan --link-destcara yang tidak disengaja (yang mungkin atau mungkin tidak menyebabkan komplikasi), membutuhkan mengetahui (atau menentukan) jalur absolut ke sumber (sebagai argumen untuk --link-dest), dan sekali lagi meninggalkan struktur direktori kosong untuk dibersihkan sebagai per 1 .
  • Anda dapat menggunakanfind untuk secara berurutan membuat ulang struktur direktori sumber pada target, kemudian secara individual memindahkan file yang sebenarnya
    ... tetapi ini harus berulang melalui sumber beberapa kali dan dapat menghadapi kondisi balapan (direktori baru sedang dibuat di sumber selama proses multi-langkah )
  • cpdapat membuat tautan keras (sederhananya, pointer tambahan ke file yang ada sama), yang menciptakan hasil yang sangat mirip dengan penggabungan mv(dan sangat IO-efisien karena hanya pointer dibuat dan tidak ada data aktual yang harus disalin)
    ... tapi ini lagi menderita kondisi ras yang mungkin (file baru di sumber yang dihapus meskipun mereka tidak disalin pada langkah sebelumnya)

Manakah dari solusi ini (jika ada) yang sesuai akan sangat tergantung pada kasus penggunaan khusus Anda.
Seperti biasa, pikirkan sebelum Anda menjalankan salah satu dari perintah ini, dan miliki cadangan.


1: Perhatikan bahwa rsync --remove-source-filestidak akan menghapus direktori apa pun, jadi Anda harus melakukan sesuatu seperti find -depth -type d -empty -deletesesudahnya untuk menyingkirkan pohon direktori sumber kosong.

n.st
sumber
Sepertinya Anda sudah mencoba satu implementasi saja mv. Jawaban ini akan lebih baik dengan kebenaran yang lebih luas. Linux, BSD dan Unix "nyata", atau referensi dari POSIX atau SUS.
Warren Young
@ WarrenYoung Anda benar, saya hanya mencoba mvimplementasi yang digunakan oleh Debian - penekanan sedang dicoba , karena halaman manual tidak menyebutkan perilaku ini ...
n.st
38
Kerugian dari rsync adalah ia benar-benar menyalin data, bukan hanya mengubah hard link, yang berpotensi sumber daya intensif jika Anda berurusan dengan banyak data.
Jonathan Mayer
7
@Keith Perhatikan bahwa --deletehanya menghapus file di direktori tujuan yang tidak ada di direktori sumber.
n.st
1
@JonathanMayer rsync sebagai beberapa fitur terkait tautan keras. Misalnya Anda hanya dapat mempertahankan tautan keras dengan -Hfungsi atau Anda dapat menghubungkan tautan di file dengan tujuan --link-dest. Lihat halaman manual sebelum menggunakannya.
allo
87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/
fazie
sumber
Apakah ini akan menghapus file sumber seperti di komentar oleh n.st?
Dominique
3
Tidak, saya lebih suka membuatnya dalam dua langkah untuk alasan keamanan. Sumber yang digabungkan dan dihapus tidak dapat dipulihkan. Langkah tambahan di n.st anwer juga diperlukan (untuk menghapus direktori).
fazie
3
--remove-source-filesmemiliki keuntungan hanya dengan menghapus file yang berhasil ditransfer, sehingga Anda dapat menggunakan finduntuk menghapus direktori kosong dan akan dibiarkan dengan semua yang tidak ditransfer tanpa harus memeriksa rsyncoutput.
n.st
4
Tapi sebenarnya tidak bergerak - dampak kecepatan sangat besar, jika file besar terlibat.
Alex
Tetapi Anda tidak dapat melakukan gerakan murni dan penggabungan sebenarnya.
fazie
64

Anda dapat menggunakan -lopsi perintah cp , yang menciptakan tautan keras file pada sistem file yang sama dan bukan salinan data lengkap. Perintah berikut menyalin folder source/folderke folder induk ( destination) yang sudah berisi direktori dengan nama folder.

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

Anda mungkin juga ingin menggunakan -P( --no-dereference- jangan menghapus tautan simbolik referensi) atau -a( --archive- mempertahankan semua metadata, juga menyertakan -Popsi), tergantung pada kebutuhan Anda.

palswim
sumber
7
@rautamiekka: Saya berasumsi Anda menanyakan alasan untuk menggunakan tautan keras. Jika Anda tidak tahu apa itu tautan keras dan mengapa Anda harus menggunakannya, maka Anda sebaiknya tidak mengambil rute ini. Namun, membuat tautan keras tidak membuat salinan lengkap, sehingga operasi ini akan mengambil pesanan dengan besaran lebih sedikit dari pada salinan lengkap. Dan, Anda akan menggunakan tautan keras alih-alih tautan lunak sehingga Anda dapat menghapus file sumber dan masih memiliki data yang benar alih-alih petunjuk ke jalur yang tidak valid. Dan cpbukannya rsynckarena setiap sistem memiliki cpdan semua orang memiliki keakraban dengannya.
palswim
7
Kecemerlangan solusi ini dapat dilihat karena tidak menjadi jawaban yang diterima. Ini adalah solusi yang elegan. Anda mendapatkan kemampuan menggabungkan cpdengan waktu operasi mv.
TheHerk
2
jika Anda tahu bahwa Anda tidak perlu memindahkan file yang sudah ada di tujuan, Anda juga ingin menambahkan-n
ndemou
1
@Ruslan: Ini benar, tetapi Anda tidak dapat bergerak tanpa menyalin seluruh sistem file dengan metode apa pun. Bahkan mv /fs1/file /fs2/(lintas filesystem) akan melakukan salinan dan kemudian menghapus.
palswim
2
Benar, tetapi sementara mvakan bekerja (asalkan target direktori belum ada) bahkan jika tidak "efisien" atau apa pun namanya, cp -rlakan gagal.
Ruslan
23

Saya akan merekomendasikan empat langkah ini:

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

atau lebih baik lagi, inilah skrip yang mengimplementasikan semantik yang mirip dengan 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
Jonathan Mayer
sumber
Args adalah SUMBER, DEST
schuess
Ini terlihat sangat berguna. Saya tergoda untuk menggunakannya untuk membersihkan hard drive saya. Bisakah ada pakar lain mengomentarinya sebelum saya mempercayakan banyak cadangan ke skrip ini? :-)
LarsH
BTW, jika Anda ingin melakukan yang setara dengan rsync -u(hanya perbarui jika lebih baru), mv(dalam beberapa versi setidaknya) juga dapat mengambil -uopsi. Namun dalam hal ini Anda mungkin ingin menghapus direktori sumber yang tidak kosong dan juga direktori yang kosong, untuk menutup kasus-kasus di mana file dalam pohon sumber tidak lebih baru. @ schuess: Sepertinya ada beberapa argumen SUMBER, jika Anda membutuhkannya.
LarsH
1
Ini tidak menangani spasi dengan baik. Saya mencobanya dengan beberapa direktori dengan spasi di dalamnya dan berakhir dengan serangkaian direktori bersarang yang tak terbatas.
rofer
16

Berikut adalah cara yang akan menggabungkan direktori. Ini jauh lebih cepat daripada rsync karena hanya mengganti nama file daripada menyalinnya dan kemudian menghapusnya.

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'
Permata
sumber
Itu menarik tetapi hanya sedikit relevan dengan topik dan bahkan tidak sedikit pun dari apa yang ditanyakan pengguna.
Shadur
17
Sebenarnya, kode Jewel melakukan persis apa yang diminta pengguna, dengan pengecualian membuat direktori yang hilang. Mungkin Anda harus melihat lagi?
Jonathan Mayer
3
Saya akan menambahkan untuk menggunakan "-print0" di find dan "-0" di xargs karena ada file yang memiliki spasi di namanya. Juga, ada masalah kecil, jika sebuah nama mengandung tanda kurung mereka tidak akan dipindahkan.
markuz
2
Ini jauh lebih cepat daripada rsync untuk sejumlah kecil file, tetapi ini mem-proses proses baru untuk setiap file, sehingga kinerjanya tidak bagus dengan sejumlah besar file kecil. @ palswim tidak menderita masalah ini.
b0fh
2
Perintah akan gagal, jika dalam destsudah direktori dengan nama yang sama seperti pada source. Dan file akan dipindahkan ke dest, yang ada di source. Perintah itu tidak lebih darimv source/* source/dest/.
ceving
3

Salah satu cara untuk mencapai ini adalah dengan menggunakan:

mv folder/* directory/folder/
rmdir folder

Selama tidak ada dua file memiliki nama yang sama folderdan directory/folder, Anda akan mencapai hasil yang sama yaitu penggabungan.

asheeshr
sumber
3
Bagaimana tepatnya cara rm folderkerjanya?
JakeGould
5
@ JakeGould Tidak sama sekali. :)
n.st
rm folder -fRselalu bekerja untukku
Octopus
2
Ketahuilah bahwa ini tidak akan berfungsi untuk file tersembunyi
b0fh
2

Untuk salinan yang paling murni, saya menggunakan metode salin blockread tar (-) B.

contoh, dari dalam jalur sumber ('cd' di sana jika perlu):

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

ini membuat salinan persis pohon sumber, DENGAN pemilik dan izin utuh. Dan jika folder target ada, data akan digabungkan. Hanya file yang sudah ada yang akan ditimpa.

Contoh:

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

Ketika tindakan salin berhasil, Anda dapat menghapus sumber ( rm -rf <source>). Tentu saja ini bukan langkah yang tepat: data akan disalin, sampai Anda menghapus sumbernya.

Sebagai opsi, Anda dapat menggunakan verbose (menampilkan pada layar file yang sedang disalin), dengan -v: tar cBvf -

  • c: buat
  • B: baca blok penuh (untuk pipa baca)
  • v: verbose
  • f: file untuk ditulis
  • x: ekstrak
  • -: stdout / stdin

sourcefolderbisa juga *(untuk apa pun di folder saat ini)

gjs
sumber
Menentukan f -tar biasanya tidak diperlukan - standarnya adalah membaca dari stdin / write ke stdout.
muru
1

Berikut ini skrip yang berfungsi untuk saya. Saya lebih suka mv daripada rsync, jadi saya menggunakan solusi Jewel dan 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
xer0x
sumber
Solusi ini tidak keluar dengan benar dari nama jalur, hati-hati.
user12439
@ user12439, saya akan memperbarui solusinya jika Anda menunjukkan kepada saya bagian mana yang harus diperbaiki.
xer0x
1

Bukan ide yang baik untuk menggunakan perintah seperti cp atau rsync. Untuk file besar, itu akan memakan waktu lama. mv jauh lebih cepat karena hanya memperbarui inode tanpa menyalin file secara fisik. Opsi yang lebih baik adalah menggunakan pengelola file sistem operasi Anda. Untuk Opensuse, ada pengelola file bernama Konquerer. Itu dapat memindahkan file tanpa benar-benar menyalinnya. Ini memiliki fungsi "memotong dan menempel" seperti di Windows. Cukup pilih semua sub-direktori di direktori A. Klik kanan dan "pindah ke" direktori B yang mungkin berisi sub-direktori dengan nama yang sama. Itu akan menggabungkan mereka. Ada juga opsi apakah Anda ingin menimpa atau mengganti nama file dengan nama yang sama.

simon
sumber
1
OP bertanya apa yang terjadi ketika mvdigunakan.
don_crissti
0

Solusi python

Karena saya tidak dapat menemukan solusi yang sudah ada yang memuaskan, saya memutuskan untuk membuat skrip Python cepat untuk mencapainya.

Secara khusus, metode ini efisien karena hanya berjalan pohon sumber file sekali dari bawah ke atas.

Ini juga akan memungkinkan Anda untuk dengan cepat men-tweak hal-hal seperti penanganan file overwrite sesuai keinginan Anda.

Pemakaian:

move-merge-dirs src/ dest/

akan memindahkan semua konten src/*ke dest/dan src/akan menghilang.

move-merge-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 hulu .

Lihat juga: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-atau-move-with-replace-from-the-windows-command

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
sumber
0

Ini adalah perintah untuk memindahkan file & folder ke tujuan lain:

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

Ingat : mvperintah tidak akan berfungsi jika folder tersebut adalah b̲e̲i̲n̲g m̲e̲r̲ge̲d̲ (yaitu folder lain dengan nama yang sama sudah ada di tujuan) dan d̲e̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲pt̲y .

mv: tidak dapat memindahkan '/ source / path / folder' ke '/ target / destination / folder': Direktori tidak kosong

Jika folder tujuan kosong, perintah di atas akan berfungsi dengan baik.

Jadi, untuk menggabungkan kedua folder dalam hal apa pun,
lakukan dalam 2 perintah:

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

Atau gabungkan keduanya sebagai perintah satu kali:

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

mv = pindah
cp = salin
rm = hapus

-r untuk direktori (folder)
-f eksekusi paksa

Talha Siddiqi
sumber