Apa yang dilakukan `mv ./*` tanpa menentukan tujuan?
27
Saya tidak sengaja lupa menentukan tujuan sebelum menekan tombol Return. Di mana mv ./*tanpa menentukan tujuan memindahkan file dan direktori di bawah direktori saat ini?
"Aku terkejut mvmenerima 1 argumen." Tidak. Ia menerima semua argumen yang diberikan shell setelah memperluas *.
glglgl
Jawaban:
41
Jika argumen terakhir adalah direktori, Anda baru saja memindahkan semua file dan direktori di direktori kerja Anda saat ini (kecuali yang namanya dimulai dengan titik) ke direktori itu. Jika ada dua file, file pertama mungkin telah menimpa file kedua.
Berikut ini beberapa demonstrasi:
Lebih dari dua file dan argumen terakhir adalah file
$ mkdir d1 d2 d3
$ touch a b c e
$ mv *
mv: target 'e' is not a directory
Lebih dari dua file dan argumen terakhir adalah direktori
$ mkdir d1 d2 d3
$ touch a b c
$ mv -v *
'a' -> 'd3/a'
'b' -> 'd3/b'
'c' -> 'd3/c'
'd1' -> 'd3/d1'
'd2' -> 'd3/d2'
Dua file
$ touch a b
$ mv -v *
'a' -> 'b'
Penjelasan lebih lanjut
Shell memperluas glob ( *) menjadi argumen untuk mv. Glob biasanya diperluas dalam urutan abjad. mvselalu melihat daftar file dan direktori. Ia tidak pernah melihat gumpalan itu sendiri.
Perintah ini mvmendukung dua jenis pemindahan. Satu adalah mv file ... directory. Yang lainnya adalah mv old-file-name new-file-name(atau mv old-file-name directory/new-file-name).
Pertama saya akan membuat basis tes - 5 file dan satu folder:
touch file1 file2 file3 file4 file5
mkdir folder
Selanjutnya saya akan menjalankan perintah tes. The -vmenspesifikasikan pilihan yang saya ingin setiap perintah mengeksekusi shell yang akan dicetak ke stderr. The -xpilihan menentukan bahwa saya menginginkan hal yang sama dicetak ke stderr- tapi aku ingin itu dilakukan setelah perintah dievaluasi tapi sebelum shell berjalan itu.
Jadi Anda melihat bahwa perintah yang saya beri makan adalah shell echo mv *dan perintah yang dijalankan oleh shell setelah* diperluas echo mvdiikuti oleh semua file dan folder.
Secara default shell akan memperluas gumpalan seperti:
Jadi, ketika Anda menjalankan perintah di shell yang dikonfigurasi dengan opsi default seperti mv *shell memperluas ke *kata daftar argumen semua file di direktori saat ini diurutkan sesuai dengan lokal. Itu melakukan syscall exec(ve)untuk mv(pada dasarnya) dengan daftar argumen ini ditambahkan. Jadi mvdapatkan semua argumen saat shell menggumpal dan mengurutkannya. Selain melakukan straceuntuk melihat efek ini, Anda dapat menggunakan debug lagi seperti:
sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT
Pada dasarnya, shell mengeksekusi mvdengan isi direktori (jika tidak kosong dan tidak termasuk file / folder dengan nama yang dimulai dengan .) sebagai daftar argumennya. mvadalah POSIX ditentukan untuk menafsirkan argumen akhir sebagai direktori jika dipanggil dengan lebih dari dua argumen - dengan cara yang sama lnadalah (karena, pada kenyataannya, mereka alat yang sangat serupa dalam mendasari fungsi) .
Dalam bentuk sinopsis pertama, mvutilitas akan memindahkan file yang dinamai oleh operan source_file ke tujuan yang ditentukan oleh target_file . Bentuk sinopsis pertama ini diasumsikan ketika operan terakhir tidak menamai direktori yang ada dan bukan tautan simbolis yang merujuk ke direktori yang ada. Dalam hal ini, jika source_file menamai file non-direktori dan target_file diakhiri dengan /slashkarakter tambahan, mvakan menganggap ini sebagai kesalahan dan tidak ada operan source_file yang akan diproses.
Dalam bentuk sinopsis kedua, mvharus memindahkan setiap file yang dinamai oleh operan source_file ke file tujuan di direktori yang ada yang dinamai oleh operan target_dir , atau dirujuk jika target_dir adalah tautan simbolik yang merujuk ke direktori yang ada. Jalur tujuan untuk setiap source_file harus merupakan gabungan dari direktori target, /slashkarakter tunggal jika target tidak diakhiri dengan /slash, dan komponen pathname terakhir dari source_file . Bentuk kedua ini diasumsikan ketika operan terakhir menamai direktori yang ada.
Jadi jika *diperluas ke:
dua file
Anda seharusnya hanya memiliki satu file, yang merupakan file pertama renamedhingga yang kedua setelah yang kedua unlinked.
satu atau lebih file diikuti terakhir oleh direktori atau tautan ke satu
Anda seharusnya hanya memiliki satu direktori atau tautan ke satu, yang merupakan tempat di mana semua konten orang tuanya sebelumnya baru saja dipindahkan.
ada yang lain
Anda harus memiliki pesan kesalahan dan napas lega yang memuaskan.
ya, menggunakan yang lama sh, saya lupa tentang debug dengan itu, tetapi mengenai pertanyaan awal saya, itu berarti masalahnya adalah shell bukan mv? Itu menjijikkan, ini lebih sederhana dari yang saya pikirkan, tetapi itu adalah jebakan yang nyata.
user2485710
5
@ user2485710, titik kuncinya adalah bahwa di Unix, shell bertanggung jawab untuk memperluas wildcard sebelum meneruskan argumen baris perintah ke perintah. Di Windows, setiap perintah harus memperluas wildcard itu sendiri. Ini memungkinkan Unix shells untuk menawarkan fasilitas wildcard canggih yang berfungsi dengan perintah apa pun.
cjm
2
@ mikeserv mengingat fakta bahwa file-file itu tidak begitu penting. Aku bergegas membersihkan dan melompat ke langkah berikutnya, tapi aku bisa mengkonfirmasi hal-hal seperti sekarang muncul di edit posting pertama / pertanyaan saya.
user2485710
6
@ mikeserv tl; dr, *bukan satu argumen? Kanan? :)
Bernhard
1
@ Bernhard - sebenarnya, itu satu-satunya kasus yang tidak saya bahas - karena mungkin saja.
mikeserv
18
Pertama, shell mengembang ./*ke semua file di direktori saat ini (kecuali file yang dimulai dengan titik).
jika tidak ada atau hanya satu file: mvgagal
jika ada dua file: yang pertama dipindahkan ke yang kedua (yang karenanya hilang)
jika ada lebih dari dua file:
jika yang terakhir adalah direktori: semua file dipindahkan ke direktori ini
Terima kasih. bagaimana cara mengetahui subdirektori mana yang "yang terakhir"?
Tim
7
Panggil saja echo ./*apakah melihat urutan menggunakan shell Anda (biasanya alfabet).
jofel
Jika gagal dengan satu file mengapa tidak dikatakan demikian?
Noumenon
@ Noumenon Saya tidak mengerti komentar Anda. mvmengembalikan pesan kesalahan jika dipanggil hanya dengan satu argumen.
jofel
Kamu benar. Perintah yang sama dari sejarah saya sekarang memberi missing destination file operand after *filename*meskipun kemarin tidak.
Noumenon
4
Saat Anda mengetik mv ./*, shell Anda akan mengembang ./*sebelum mengeksekusi mv.
Beberapa hal yang perlu diperhatikan:
Jika ./*diperluas menjadi kurang dari 2 argumen, mvakan, secara logis, menghasilkan kesalahan.
./* biasanya akan diperluas ke setiap file (termasuk direktori) yang ada di direktori saat ini dan tidak dimulai dengan titik.
Anda dapat mengontrol ./*ekspansi apa dengan membaca dokumentasi shell Anda ( man 7 globmerupakan titik masuk ke topik). Kerang yang berbeda akan memiliki opsi yang berbeda.
Perintah mv file1 file2 ... filen directoryini sangat kecil kemungkinannya ada hubungannya sama sekali *.
mikeserv
@ Mike: Jawaban saya menunjukkan bahwa tahap terakhir ekspansi shell secara efektif mengubah yang terakhir menjadi yang sebelumnya. Tidak mengetahui ini tampaknya menjadi akar kebingungan OP.
RedGrittyBrick
ya, tapi poin saya adalah shell tidak akan memperluas *ke file dirkecuali ada beberapa lokal di mana dberikut f.
mikeserv
@ mikeserv: Ada lokal seperti itu, contoh saya adalah cut & paste dari Putty terhubung ke sistem CentOS 5.6 GNU / Linux.
RedGrittyBrick
lokal mana itu? Contoh Anda adalah a b c dyang tidak file ... dir. Saya hanya berkomentar sama sekali karena Anda tidak menyebutkan hal semacam itu di mana pun dan hanya mengatakan itu *menjadi file ... diryang tidak terjadi.
mv
menerima 1 argumen." Tidak. Ia menerima semua argumen yang diberikan shell setelah memperluas*
.Jawaban:
Jika argumen terakhir adalah direktori, Anda baru saja memindahkan semua file dan direktori di direktori kerja Anda saat ini (kecuali yang namanya dimulai dengan titik) ke direktori itu. Jika ada dua file, file pertama mungkin telah menimpa file kedua.
Berikut ini beberapa demonstrasi:
Lebih dari dua file dan argumen terakhir adalah file
Lebih dari dua file dan argumen terakhir adalah direktori
Dua file
Penjelasan lebih lanjut
Shell memperluas glob (
*
) menjadi argumen untukmv
. Glob biasanya diperluas dalam urutan abjad.mv
selalu melihat daftar file dan direktori. Ia tidak pernah melihat gumpalan itu sendiri.Perintah ini
mv
mendukung dua jenis pemindahan. Satu adalahmv file ... directory
. Yang lainnya adalahmv old-file-name new-file-name
(ataumv old-file-name directory/new-file-name
).sumber
Pertama saya akan membuat basis tes - 5 file dan satu folder:
Selanjutnya saya akan menjalankan perintah tes. The
-v
menspesifikasikan pilihan yang saya ingin setiap perintah mengeksekusi shell yang akan dicetak kestderr
. The-x
pilihan menentukan bahwa saya menginginkan hal yang sama dicetak kestderr
- tapi aku ingin itu dilakukan setelah perintah dievaluasi tapi sebelum shell berjalan itu.KELUARAN
Jadi Anda melihat bahwa perintah yang saya beri makan adalah shell
echo mv *
dan perintah yang dijalankan oleh shell setelah*
diperluasecho mv
diikuti oleh semua file dan folder.Secara default shell akan memperluas gumpalan seperti:
KELUARAN
Ini adalah hasil dari
set [+-]f
fungsi glob:KELUARAN
Jadi, ketika Anda menjalankan perintah di shell yang dikonfigurasi dengan opsi default seperti
mv *
shell memperluas ke*
kata daftar argumen semua file di direktori saat ini diurutkan sesuai dengan lokal. Itu melakukan syscallexec(ve)
untukmv
(pada dasarnya) dengan daftar argumen ini ditambahkan. Jadimv
dapatkan semua argumen saat shell menggumpal dan mengurutkannya. Selain melakukanstrace
untuk melihat efek ini, Anda dapat menggunakan debug lagi seperti:KELUARAN
Dan mudah dibawa:
KELUARAN
Pada dasarnya, shell mengeksekusi
mv
dengan isi direktori (jika tidak kosong dan tidak termasuk file / folder dengan nama yang dimulai dengan.
) sebagai daftar argumennya.mv
adalah POSIX ditentukan untuk menafsirkan argumen akhir sebagai direktori jika dipanggil dengan lebih dari dua argumen - dengan cara yang samaln
adalah (karena, pada kenyataannya, mereka alat yang sangat serupa dalam mendasari fungsi) .Cukup
echo
es saja:KELUARAN
Semua file dipindahkan ke argumen akhir - karena itu adalah folder. Sekarang bagaimana jika itu bukan folder?
KELUARAN
Beginilah cara POSIX menentukan
mv
perilaku dalam kasus itu:Jadi jika
*
diperluas ke:dua file
renamed
hingga yang kedua setelah yang keduaunlinked
.satu atau lebih file diikuti terakhir oleh direktori atau tautan ke satu
ada yang lain
sumber
sh
, saya lupa tentang debug dengan itu, tetapi mengenai pertanyaan awal saya, itu berarti masalahnya adalah shell bukanmv
? Itu menjijikkan, ini lebih sederhana dari yang saya pikirkan, tetapi itu adalah jebakan yang nyata.*
bukan satu argumen? Kanan? :)Pertama, shell mengembang
./*
ke semua file di direktori saat ini (kecuali file yang dimulai dengan titik).mv
gagalmv
gagal.sumber
echo ./*
apakah melihat urutan menggunakan shell Anda (biasanya alfabet).mv
mengembalikan pesan kesalahan jika dipanggil hanya dengan satu argumen.missing destination file operand after *filename*
meskipun kemarin tidak.Saat Anda mengetik
mv ./*
, shell Anda akan mengembang./*
sebelum mengeksekusimv
.Beberapa hal yang perlu diperhatikan:
./*
diperluas menjadi kurang dari 2 argumen,mv
akan, secara logis, menghasilkan kesalahan../*
biasanya akan diperluas ke setiap file (termasuk direktori) yang ada di direktori saat ini dan tidak dimulai dengan titik../*
ekspansi apa dengan membaca dokumentasi shell Anda (man 7 glob
merupakan titik masuk ke topik). Kerang yang berbeda akan memiliki opsi yang berbeda.sumber
Inilah jawaban yang lebih singkat:
Shell memperluas wildcard
*
ke daftar isi direktori. Kemudian shell melewati daftar lengkap itu ke perintah. Perintah itu tidak pernah melihat*
.Perintah
mv file1 file2 ... filen directory
akan memindahkan file1 ... filen ke direktori.Contoh
Di sini saya membuat direktori pengujian yang berisi tiga file
Anda tidak dapat memindahkan banyak file ke satu file
Mari kita tambahkan subdirektori
Anda dapat memindahkan banyak file ke subdirektori
sumber
mv file1 file2 ... filen directory
ini sangat kecil kemungkinannya ada hubungannya sama sekali*
.*
kefile dir
kecuali ada beberapa lokal di manad
berikutf
.a b c d
yang tidakfile ... dir
. Saya hanya berkomentar sama sekali karena Anda tidak menyebutkan hal semacam itu di mana pun dan hanya mengatakan itu*
menjadifile ... dir
yang tidak terjadi.