Bagaimana cara menimpa file target dengan mv?

155

Saya punya banyak file dan direktori dalam subdirektori yang ingin saya pindahkan ke direktori induk. Sudah ada beberapa file dan direktori di direktori target yang perlu ditimpa. File yang hanya ada di target harus dibiarkan tidak tersentuh. Bisakah saya memaksakan mvuntuk melakukan itu? Itu ( mv * ..) mengeluh

mv: cannot move `xyz' to `../xyz': Directory not empty

Apa yang saya lewatkan?

EricSchaefer
sumber
3
Sudahkah Anda mencoba mv -f?
sakisk
Saya bertanya-tanya mengapa mv -fjawaban itu tidak benar.
Pedro Lobito
@PedroLobito: Karena tidak berfungsi? -f hanya menimpa file, tetapi tidak berfungsi jika Anda memindahkan subdirektori yang ada di tujuan dan tidak kosong.
EricSchaefer

Jawaban:

118

Anda harus menyalinnya ke tujuan dan kemudian menghapus sumbernya, menggunakan perintah yang cp -r * ..diikuti oleh rm -rf *.

Saya tidak berpikir Anda dapat "menggabungkan" direktori menggunakan mv.

dogbane
sumber
4
Yah, itu yang tidak ingin saya lakukan, karena itu akan memakan waktu lama ... Terima kasih.
EricSchaefer
12
Agaknya mvlebih cepat karena Anda berada di sistem file yang sama? Bagaimana jika Anda menggunakan cp -luntuk membuat hardlink daripada benar-benar memindahkan file?
mattdm
6
Anda harus menggunakan cp -aalih-alih cp -runtuk mempertahankan atribut file (cap waktu, izin, dll.).
dotancohen
3
Bagi orang-orang yang datang terlambat ke sini melalui google, jawaban di bawah ini oleh @palswim mengemulasi perilaku mv dengan membuat tautan keras baru ke data dan kemudian menghapus tautan lama. Jawaban singkat cp -rl source destination && rm -r source.
William Everett
72

rsyncmungkin akan menjadi pilihan yang lebih baik di sini. Sesederhana itu rsync -a subdir/ ./.

Pohon tes saya dalam filename: contentsformat:

./file1:root
./file2:root
./dir/file3:dir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

Berlari rsync:

$ rsync -a -v subdir/ ./
sending incremental file list
./
file1
dir/
dir/file3

Memberi:

./file1:subdir
./file2:root
./dir/file3:subdir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

Dan kemudian, untuk meniru mv, Anda mungkin ingin menghapus direktori sumber:

$ rm -r subdir/

Memberi:

./file1:subdir
./file2:parent
./dir/file3:subdir
./dir/file4:dir

Jika ini salah, dapatkah Anda memberikan contoh serupa (mis. Menggunakan pohon pengujian saya dari dekat bagian atas jawaban ini) dengan hasil yang diinginkan?

Mikel
sumber
1
salinan rsync. Pertanyaan ini tentang pindah.
Gilles
1
@Gilles: Terima kasih. Saya menambahkan rm -rpada akhirnya untuk membuatnya pada dasarnya sama dengan mv.
Mikel
3
copy-then-delete tidak setara dengan mv ketika sumber dan tujuan berada pada sistem file yang sama. mvbersifat atomik, mempertahankan nomor inode (agar file dapat tetap terbuka), dan tidak memerlukan waktu dan ruang untuk membuat salinan.
Gilles
5
@Gilles: Saya menyadari itu, tetapi saat ini jawabannya adalah cp -r; rm -r. Saya pikir dalam pengertian itu, rsynclayak disebut juga.
Mikel
Saya sudah melakukannya dengan cp / rm (sangat mendesak). Butuh waktu lama. Naskah Gilles mungkin akan jauh lebih cepat, tetapi dia sudah terlambat juga.
EricSchaefer
47

rsyncdapat menghapus sumber setelah salinan dengan --remove-source-filesparameter. Ini harus menjadi cara yang nyaman untuk melakukan apa yang Anda inginkan.

Dari rsync man page:

        --remove-source-files   sender removes synchronized files (non-dir)
Mike Chen
sumber
Ini benar-benar jawaban terbaik. Saya tidak dapat menggunakan cp -r; rmkarena kurangnya ruang kosong. Alih-alih rsync --remove-source-fileskedua ruang disk yang diperkecil dihindari menghindari menyalin file yang sama persis.
guaka
20

Anda dapat melakukan ini dengan cpdan rm, tetapi tanpa menyalin sejumlah besar data Anda (mungkin) berusaha menghindari transfer. @mattdm menyinggung ini dalam komentarnya , dan jawaban untuk pertanyaan lain memiliki diskusi yang lebih lengkap tentang berbagai opsi.

cp -rlf source destination
rm -r source

Pada dasarnya, -lopsi untuk cpperintah ini menciptakan tautan keras ke file daripada menyalin data mereka ke file baru.

palswim
sumber
1
Saya tahu OP sudah menyelesaikan tugasnya yang memicu pertanyaan, tapi mudah-mudahan jawaban ini dapat membantu siapa pun dengan masalah ini di masa depan.
palswim
Ini benar-benar jawaban yang mereka butuhkan. Saya hanya melakukan ini dengan 60GB ribuan file email cyrus kecil dan hanya butuh 21 detik.
labradort
Ini juga rute yang saya ambil - Saya baru saja mengubahnya cp -al source destinationuntuk menjaga info dan izin pemilik.
piit79
Saya pikir untuk benar-benar melakukan apa yang diminta OP untuk Anda harus menambahkan opsi -f jika tidak akan menimpa jika file ada. Bisakah Anda mengkonfirmasi itu atau saya melakukan sesuatu yang salah?
das Keks
@dasKeks: Sebenarnya, cpmenimpa secara default. The -fpilihan mencoba untuk menghapus file (seperti dalam rm) sebelum mencoba untuk menyalin lagi, yang dapat membantu jika proses tidak dapat membuka file untuk menulis, meskipun beberapa orang akan lebih memilih untuk melihat bahwa ada kesalahan sebagai gantinya. Saya tidak tahu apakah itu penting bagi OP, tetapi saya tetap menambahkan -fbendera pada jawaban saya.
palswim
8

Berikut ini skrip yang memindahkan file dari bawah /path/to/source/rootke jalur yang sesuai di bawah /path/to/destination/root.

  • Jika ada direktori di sumber dan tujuan, konten dipindahkan dan digabungkan secara rekursif.
  • Jika file atau direktori ada di sumber tetapi tidak di tujuan, itu dipindahkan.
  • File atau direktori apa pun yang sudah ada di tujuan tertinggal. (Khususnya, direktori gabungan tertinggal di sumbernya. Ini tidak mudah untuk diperbaiki.)

Hati-hati, kode yang belum diuji.

export dest='/path/to/destination/root'
cd /path/to/source/root
find . -type d \( -exec sh -c '[ -d "$dest/$0" ]' \; -o \
                  -exec sh -c 'mv "$0" "$dest/$0"' {} \; -prune \) \
    -o -exec sh -c '
        if ! [ -e "$dest/$0" ]; then
          mv -f "$0" "$dest/$0"
        fi
' {} \;
Gilles
sumber
Dua koreksi: Anda perlu \;sebelum -odi baris pertama dari findperintah, dan Anda tidak harus melarikan diri !di if- itu hanya !, tidak\!
llhuii
2

Utas ini sudah ada selama bertahun-tahun dan masih menempati peringkat # 1 di google jadi saya ingin menambahkan metode lain. Bagaimana saya biasanya melakukan ini: mengemas konten subdir ke dalam tarball, memindahkan tarball ke direktori induk dan kemudian mengekstraknya dengan perilaku default --overwrite. Ini tepat seperti yang Anda cari. Setelah itu, Anda dapat menghapus subdir.

cd xyz
tar -cvzpf tmp.tar.gz *
mv tmp.tar.gz ../tmp.tar.gz
cd ..
tar -xvzpf tmp.tar.gz
rm -rf xyz
rm -f tmp.tar.gz
Simon Kraus
sumber
Ini hanya berfungsi jika Anda memiliki ruang ekstra untuk file tar terkompresi. Dan apakah Anda benar-benar ingin menyimpan file tar sementara setelah ekstraksi?
Anthon
Kode yang diedit untuk menghapus file tmp. Saat ini ruang tidak menjadi masalah dalam banyak kasus. #terabyteages
Simon Kraus
0

Jika Anda memiliki cukup penyimpanan, Anda dapat melakukannya dengan cara berikut:

mv -bfv directory_1/* directory_2/ # all duplicate source files/directories 
                                   # will have ~ appended to them
find -name "*~" -delete            # will recursively find and delete all files 
                                   # with ~ on the end

Pastikan tidak ada file penting dengan tanda ~ di ujungnya, tetapi jika ada, Anda dapat menambahkan --suffix=whateveryouwantalih-alih default.

4D3H2
sumber