Bagaimana mencegah `mv` dari memindahkan kumpulan file menjadi satu file biasa?

17

Saya baru saja kehilangan sebagian kecil dari koleksi audio saya, karena kesalahan bodoh yang saya buat. :-(
SANGAT SAYA punya cadangan yang cukup baru, tapi itu masih menjengkelkan. Terlepas dari Anda benar-benar, penyebab lain melakukan kejahatan itu mv, yang akan ditampilkan sebagai berikut:

File audio memiliki skema tertentu:

ARTIST - Some Title YY.mp3

di mana YYspesifikasi tahun 2 digit.

mkdir 90<invisible control character>

(Sampai saat ini, saya tidak tahu bahwa saya benar-benar mengetik sepertiga karakter berlebih yang tidak terlihat ...!)
Daripada memiliki semuanya dalam satu direktori, saya ingin memiliki semua musik tahun 1990-an dalam satu direktori. Jadi saya mengetik:

find . -name '* 9?.mp3' -exec mv {} 90 \;

Tidak terlalu sulit untuk mengetahui apa yang terjadi, eh? : ->
Hasil (bencana) adalah direktori kosong perawan yang disebut '90 something '(dengan sesuatu menjadi karakter kontrol "tidak terlihat") dan satu file tunggal yang disebut' 90 ', ditimpa sebanyak n kali.

SEMUA FILE TELAH PERGI. : - (((jelas))

Wish mvakan sudah memeriksa dalam waktu apakah tanda tangan "file" tujuan (ingat pada * NIX: Everything Is A File ) dimulai dengan d------(misalnya drwxr-xr-x). Dan, tentu saja, apakah tujuan itu ada atau tidak . Ada varian dari skenario tersebut, ketika Anda hanya lupa untuk mkdirdirektori yang pertama. (tapi tentu saja, Anda berasumsi bahwa itu ada di sana ...)

Bahkan kami OS hewan peliharaan-kebencian dimulai dengan modal W APAKAH DO INI. Anda bahkan diminta untuk menentukan jenis tujuan (file? Direktori?) Jika Anda memintanya.

Oleh karena itu, saya bertanya-tanya apakah kita * NIXers masih harus menulis sendiri " mvscriptlet" hanya untuk menghindari kejutan yang tidak diinginkan semacam ini.

kesalahan sintaks
sumber
2
Tidak semua file hilang. Setidaknya satu .mp3harus ada di sana dengan nama 90, itu bisa menjadi salah satu yang Anda tidak punya cadangan.
Anthon
2
Heh, kamu punya selera humor sinis, kamu gila! :-P Nah, itu file yang disebut "satu file tunggal" dicetak tebal di OP saya. :)
syntaxerror
2
mvbukan masalah di sini, secara teknis, tidak tahu bahwa Anda memindahkan serangkaian file. Anda menjalankan mvsatu kali untuk setiap file. Begitulah cara find -exec ;kerjanya. Jika Anda menggunakan find -exec +(seperti dalam beberapa komentar) mv akan menjerit begitu mendapat lebih dari satu argumen.
Etan Reisner
Meskipun berjalan mvuntuk setiap file tunggal mungkin tampak sedikit kurang dipikirkan pada awalnya, itu akan (seperti yang saya katakan sebelumnya) menjadi satu-satunya solusi waras setelah file sumber tersebar di berbagai subdirektori. Bahwa dalam case-test saya, file-file source semuanya dalam satu direktori tidak berarti itu adalah case test saya yang sebenarnya . Ini sebenarnya hanya penyederhanaan, karena saya dapat dengan mudah menjelaskannya sendiri nanti. Plus, itu membuat pertanyaan kurang memakan waktu untuk membaca karena panjangnya berkurang. :)
syntaxerror
Mengapa Anda berharap mvmengharuskan destinasi itu ada? mv oldfile newfileadalah cara untuk mengubah nama file, dan konyol untuk berharap newfilesudah ada dan menjadi direktori.
Barmar

Jawaban:

37

Anda dapat menambahkan /ke tujuan jika Anda ingin memindahkan file ke direktori. Jika direktori tidak ada Anda akan menerima kesalahan:

mv somefile somedir/
mv: cannot move ‘somefile’ to ‘somedir/’: Not a directory

Jika ada direktori, itu memindahkan file ke direktori itu.

Marco
sumber
4
Terima kasih banyak! Itu harus menjadi penyelamat masa depan saya. I berutang budi padamu. (Sama seperti catatan, seorang teman saya telah membuat kesalahan yang sama beberapa tahun yang lalu, jadi saya merasa saya tidak sendirian.)
syntaxerror
1
Juga, ketika menggunakan Shell tertentu, Anda memiliki opsi Tab untuk melengkapi nama file untuk Anda. Jika saya tidak begitu ingat nama direktori, dan tidak ingin kekacauan seperti yang Anda miliki baru-baru ini, cukup tekan satu atau dua karakter dari nama direktori dan HIT TAB . Maka Anda dapat Yakin, bahwa itu ada, karena lengkapi-otomatis taruh di sana ....
Andyz Smith
@AndyzSmith Yah, itulah masalahnya. Anda dapat menyebutnya kebiasaan saya untuk hanya pernah menggunakan TAB untuk direktori atau jalur yang rumit , tetapi tidak untuk yang bertipe 2 huruf. :) Tapi kalau dipikir-pikir ... mungkin saya harus benar-benar mempertimbangkan kasus terakhir juga dari sekarang.
syntaxerror
20

GNU coreutils mvsudah memiliki opsi yang menyatakan bahwa Anda ingin pindah ke direktori: -t/ --target-directory. Jika argumen untuk opsi itu tidak ada, mvakan mengeluh alih-alih memindahkan semua file Anda ke nama file yang sama.

Saya akan menulis penggerak Anda sebagai berikut:

find . -name '* 9?.mp3' -exec mv -t 90 {} +

Perhatikan penggunaan +alih - alih \;, globbing sebanyak nama file sebanyak mungkin, menghasilkan eksekusi yang lebih cepat.

Anthon
sumber
Terima kasih. (Berharap itu bukan salah satu dari GNU-isme lagi, meskipun.)
syntaxerror
2
@kesalahan sintaks. Itu adalah GNUisme. POSIXly:find . -name '* 9?.mp3' -exec sh -c 'exec mv "$@" 90/' sh {} +
Stéphane Chazelas
Terima kasih banyak untuk one-liner yang paling SNEAKY ini! Anggap saja itu +5 yang kuberikan padamu. :) Akan membuat banyak upaya coba-coba yang tidak perlu .--- Dan Anda tahu betul mengapa saya membuat pernyataan itu. Hanya perlu pada mesin yang lurus POSIX (tidak jarang terjadi), dan saya akan memiliki masalah berikutnya di sana. :)
syntaxerror
1
@syntaxerror Jika jawaban ini menyelesaikan masalah Anda, silakan luangkan waktu sebentar dan klik tanda centang di bawah penghitungan suara di sebelah kiri, ini akan menandakan kepada semua orang bahwa masalah Anda telah diselesaikan dan merupakan cara terima kasih yang dinyatakan dalam situs. Saya melihat bahwa hanya beberapa dari pertanyaan Anda yang dijawab telah menerima jawaban, Anda mungkin ingin mengungkapkannya juga.
Anthon
Tidak, itu hanya tujuan. Saya sering menunggu beberapa minggu atau kadang-kadang 2 bulan sampai saya menerima jawaban. Alasannya adalah bahwa beberapa orang yang sangat berpengetahuan memiliki jadwal yang SANGAT dimuat dan mungkin hanya menemukan waktu untuk memberikan jawaban mereka (biasanya terbaik) setelah beberapa minggu. Jadi saya selalu merasa terhormat untuk menunggu mereka berhenti. Yah, dan jika mereka benar-benar tidak, saya tidak akan ragu untuk memukul tanda centang, pasti. Selain itu, saya tidak melihat mengapa beberapa orang selalu tergesa-gesa pada rasa SE +. Mudah melakukannya, kawan. Jangan lompat pistol. :) Ini bukan bos Anda yang menekan Anda.
syntaxerror
10

Selain itu, jika Anda umumnya berencana untuk menghindari overwrite yang tidak disengaja di masa mendatang, ada -iopsi untuk mv. Saya pribadi tidak bisa memikirkan kelemahannya jika Anda

alias mv='mv -i'

Jika Anda perlu menimpa sesuatu, cukup berikan -fopsi.

Alias ​​hanya berlaku jika Anda mengetik perintah langsung ke shell interaktif, bukan untuk kasus seperti doa oleh find. Anda bisa berlari

find . -name '* 9?.mp3' -exec mv -i {} 90 \;

dan kemudian Anda akan diminta jika mvmencoba menimpa file yang ada.

ayekat
sumber
4
Atau - karena Anda mungkin tidak tahu - ketik ´ \ mv `bukan mv. "Trik" yang kurang dikenal ini akan menyebabkan perintah dengan backslash didahulukan untuk mengabaikan definisi alias apa pun .
syntaxerror
Tentu saja, jika Anda benar-benar berbicara tentang find ... -exec mv ..., maka Anda harus membuat ~/bin/mv(atau direktori lain yang sesuai) dan melakukannya /bin/mv -i "$@"- karena find ... -exectidak melihat alias.
G-Man Mengatakan 'Reinstate Monica'
Dalam hal ini, saya lebih suka env mv. Kurang mengetik. :) Karena tata letak keyboard lokal saya memerlukan tombol SHIFT untuk ditekan untuk garis miring, saya akan selalu menyukai versi "tanpa garis miring" (jika ada).
syntaxerror
1
@syntaxerror: BTW, ketika Anda menanggapi komentar (dalam komentar baru), biasanya menyebutkan nama penulis, didahului dengan "@", seperti pada "@ G-Man". Dengan begitu saya mendapat pemberitahuan. (Saya dapat menanggapi komentar terakhir Anda secara semi-tepat waktu karena saya mendapat pemberitahuan dari komentar Jenny D.) Anda dapat menyingkat, atau menggunakan seluruh nama (tanpa spasi), misalnya, “@ StéphaneChazelas”. Penulis suatu posting secara otomatis diberitahu tentang komentar pada posting itu. Lihat paragraf Membalas dalam komentar di halaman bantuan ini .
G-Man Mengatakan 'Reinstate Monica'
1
Maaf, saya kurang internet selama sehari. @ G-Man Saya setuju bahwa memiliki versi pribadi mvdi lokasi di awal $PATHakan menjadi solusi yang lebih bersih. Di sisi lain saya terutama terjadi secara mvceroboh di shell interaktif (karena itu terjadi cepat). Saat saya menyusun sesuatu yang lebih kompleks, seperti findatau forloop, atau bahkan skrip shell, saya cenderung melakukan beberapa run kering (menggunakan echo) untuk memastikan saya tidak merusak sesuatu. Dalam kasus-kasus itu saya tidak perlu berpegangan tangan mv, karena saya sudah memikirkannya.
ayekat
7

Selain jawaban yang sangat baik di atas, saya ingin menjelaskan mengapa Anda tidak mendapatkan pertanyaan tentang apakah akan memindahkan file atau tidak.

Jika Anda memindahkan satu file ke nama baru, dan nama itu bukan direktori,mv akan mengubah nama file Anda ke nama baru.

Masalahnya di sini adalah bahwa Anda menggunakan finduntuk mengeksekusi mvsekali per file , bukan sekali untuk semua file .

Jika, sebaliknya, Anda sudah selesai mv *90.mp3 90, maka mv akan gagal dengan pesan kesalahan bahwa "file target bukan direktori".

Saran lain adalah menggunakan penyelesaian tab saat mengetik lintasan target. Ini akan menunjukkan kepada Anda apakah targetnya adalah direktori dengan menambahkan /ke nama target. Anda juga dapat menggunakan mv -iuntuk ditanya apakah Anda ingin menimpa file yang ada.

Jenny D
sumber
Jika, alih-alih, Anda selesai mv *90.mp3 90, maka mv akan gagal dengan pesan kesalahan bahwa "file target bukan direktori". Hah, ya, kenapa begitu rumit ya? Saya menggunakan kalimat Anda dan saya akan senang. Namun hanya dalam kasus sepele ini. :) Karena ini adalah cara normal saya mengajukan pertanyaan saya: Saya akan mempersempitnya demi kesederhanaan. Tidak ada yang keberatan findsejauh ini mengingat bahwa file 90-an mungkin juga tersebar di berbagai subdirektori yang ingin saya "tangkap" juga. Jika dan hanya jika mereka selalu ada dalam satu direktori sumber, mvbaris Anda berlaku.
syntaxerror
@syntaxerror Sangat benar - saya maksudkan ini sebagai contoh bagaimana mvberperilaku, bukan sebagai kritik terhadap pilihan alat Anda.
Jenny D
1
Anda mungkin dapat membangun sesuatu yang menggabungkan finddan mv, misalnya find /music -type d -exec mv {}/*90.mp3 targetdir\;- tetapi sekarang saya merasa agak seperti terlalu rumit, dan hanya menggunakan -iatau -tlebih efisien
Jenny D
1
@ Jenny: Jika find . -type d -exec mv {}/*9?.mp3 target \;contoh Anda berhasil, masih akan ada risiko bahwa mvperintah akan terlihat seperti mv file targetuntuk setiap direktori yang hanya berisi satu *9?.mp3file; jadi semua file tersebut (kecuali yang terakhir) akan hilang.
G-Man Mengatakan 'Reinstate Monica'
4
@syntaxerror: Saya salut atas usaha Anda untuk mengekspos bagian penting dari masalah Anda, daripada seluruh 42.000 skrip baris tempat masalah itu muncul. Tetapi, bahkan jika Anda memang ingin melakukan sesuatu untuk semua *.mp3file di pohon direktori, Anda bisa shopt -s globstardan kemudian menjalankan perintah Anda **/*.mp3- **akan bertindak seperti a find.
G-Man Mengatakan 'Reinstate Monica'
1

Sebagai strategi tujuan umum alternatif, saya ingin menyarankan untuk mengubah operasi semacam ini menjadi skrip sementara. Saya lebih suka melihat hasil finddan mengubahnya menjadi mvperintah dengan tangan, memastikan bahwa saya mengerti apa yang saya lakukan sebelum saya jalankan. misalnya.

find . -name '* 9?.mp3' > tmp
vim tmp

Sekarang saya dapat melihat daftar nama file dan menulis ulang isi file sebagai perintah shell.

  • Letakkan konten file pada satu baris: ggVGJ
  • Prepend: Imv [esc]
  • Menambahkan: Asomedir/ [esc]
  • Simpan file. Baca lagi. Mengambil napas.
  • Jalankan source tmpdi baris perintah.

Ini adalah strategi konservatif, tetapi saya sudah terlalu sering digigit oleh salah ketik -execataused perintah yang perintah, atau ekspansi shell yang disalahpahami, dan saya lebih suka mengambil pendekatan yang lambat dan konsisten.

Dengan kata lain: Saya terlalu pengecut untuk digunakan -exec.

Tom Rees
sumber
1
Anda mengabaikan :%s/.*/"&"/langkah - karena, dalam hal ini, Anda tahu bahwa setiap nama file mengandung setidaknya satu ruang.
G-Man Mengatakan 'Reinstate Monica'
1
Ini akan menggigit Anda jika nama file berisi spasi atau karakter khusus lainnya. Anda perlu mengutip mereka dengan benar. Meninjau daftar perintah tidak terlalu mungkin untuk menangkap kesalahan. Ada jauh lebih baik cara untuk meninjau perintah sebelum menjalankannya, seperti menjalankan echo mvalih-alih mv, dan kemudian menghapus echojika Anda senang.
Gilles 'SO- stop being evil'
Menemukan kesalahan seperti nama file-dengan-spasi adalah teknik yang akan membantu :-)
Tom Rees
0

Pilihan lain:

-n, --no-clobber jangan menimpa file yang sudah ada

itu sama dengan -i, tapi tidak akan bertanya, itu akan gagal.

Jorge Nerín
sumber