mv: Pindahkan file hanya jika tujuan tidak ada

44

Dapatkah saya menggunakan mv file1 file2cara yang hanya bergerak file1untuk file2jika file2tidak ada?

Saya sudah mencoba

yes n | mv -i file1 file2

(ini memungkinkan mvbertanya apakah file2 harus diganti dan secara otomatis menjawab tidak) tetapi selain menyalahgunakannya -ijuga tidak memberi saya kode kesalahan yang bagus (selalu 141 bukannya 0 jika dipindahkan dan sesuatu yang lain jika tidak dipindahkan)

Fabian Schmitthenner
sumber
3
Anda harus memiliki pipefailopsi sebagai 141 akan menjadi status keluar yes, bukan mvyang tidak punya alasan untuk mendapatkan SIGPIPE di sini.
Stéphane Chazelas
Pendekatan itu juga gagal jika file2 adalah direktori (itu akan memindahkan file1 ke direktori file2). GNU mv memiliki -Tuntuk itu.
Stéphane Chazelas
@ StéphaneChazelas Jika keinginan adalah untuk menggunakan status keluar mvdaripada daripada itu yes, solusi paling sederhana mungkinmv -i file1 file2 < <(yes n)
kasperd

Jawaban:

63

mv -vn file1 file2. Perintah ini akan melakukan apa yang Anda inginkan. Anda dapat melewati -vjika Anda mau.

-v membuatnya menjadi verbose - mv akan memberi tahu Anda bahwa itu memindahkan file jika memindahkannya (berguna, karena ada kemungkinan file tidak akan dipindahkan)

-n hanya bergerak jika file2 tidak ada.

Harap dicatat, bahwa ini bukan POSIX seperti yang disebutkan oleh ThomasDickey .

MatthewRock
sumber
2
Namun, itu bukan POSIX .
Thomas Dickey
1
@ThomasDickey, apakah POSIX mendukung ini dengan cara atom?
Fabian Schmitthenner
3
to @Fabian: Mungkin tidak, tetapi bahkan dalam jawaban yang disarankan ada kemungkinan perlombaan dalam alat, tergantung bagaimana mereka ditulis.
Thomas Dickey
3
ini tampaknya tidak bebas ras, stracemenunjukkan bahwa ia menggunakan (pada sistem saya): stat ("file2", 0x7ffe3e705d10) = -1 ENOENT (Tidak ada file atau direktori seperti itu) lstat ("file1", {st_mode = S_IFREG | 0644, st_size = 0, ...}) = 0 lstat ("file2", 0x7ffe3e705a10) = -1 ENOENT (Tidak ada file atau direktori seperti itu) ganti nama ("file1", "file2") = 0 lseek (0, 0, SEEK_CUR) = -1 ESPIPE (Pencarian ilegal). Jadi mengganti nama tampaknya digunakan. @ StéphaneChazelas solusi tampaknya menjadi yang tepat jika Anda benar-benar ingin melakukannya dengan bebas.
Fabian Schmitthenner
2
Saya bertanya-tanya mengapa itu tidak digunakanrenameat2
Fabian Schmitthenner
16

mv -n

Dari man mvpada sistem GNU:

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

Pada sistem FreeBSD:

-nJangan menimpa file yang ada. (Opsi -n mengesampingkan opsi -f atau -i sebelumnya.)

Dani_l
sumber
10
if [ ! -e file2 ] && [ ! -L file2 ]
then
    mv file1 file2
# else echo >&2 there is already a file2 file.
fi

Atau:

if ! ls -d file2 > /dev/null 2>&1
then
    mv file1 file2
fi

Hanya akan berjalan mvjika file2tidak ada. Perhatikan bahwa itu tidak menjamin bahwa file2tidak akan ditimpa karena file2bisa saja dibuat antara tes dan mv, tetapi perhatikan bahwa setidaknya versi GNU saat ini mvdengan -iatau -ntidak memberikan jaminan itu baik (meskipun kondisi balapan lebih sempit di sana sejak cek dilakukan di dalam mv).

Di sisi lain, ini portabel, memungkinkan Anda untuk membedakan antara kasus, dan berfungsi terlepas dari jenis file2file (reguler, pipa, bahkan direktori ).

Majenko
sumber
3
apakah ini memperkenalkan kondisi ras di mana file dapat ditulis antara pemeriksaan keberadaan dan perpindahan?
Fabian Schmitthenner
3
Selalu ada kemungkinan apa pun yang Anda lakukan.
Majenko
3
API Linux memiliki renameat2yang dapat Anda beri RENAME_NOREPLACEbendera. Saya percaya ini secara atomis memeriksa keberadaan file dan kemudian memindahkan file.
Fabian Schmitthenner
-d untuk direktori, atau -l untuk tautan, atau bahkan -e untuk semua jenis file
Majenko
mengganti nama mungkin bebas ras tetapi sisa perintah mv tidak. Jika menurutnya itu tidak perlu dibatalkan tautannya maka tiba-tiba nama itu gagal maka akan (harus) salah.
Majenko
8

Pendekatan bebas ras dengan GNU yang lndisediakan file1bukan dari jenis direktori :

ln -PT file1 file2 && rm file1

(Kecuali untuk bug di beberapa sistem file jaringan), yang menjamin bahwa tidak ada file2file yang akan ditimpa (atau jika file2adalah tipe direktori, file1tidak akan dipindahkan ke dalamnya), karena link()panggilan sistem, bertentangan dengan rename()panggilan sistem akan gagal jika target ada.

Namun, akan ada keadaan perantara di mana file tersebut ada sebagai file1dan file2.

The -Tpilihan (untuk selalu melakukan link("file1", "file2")bahkan jika file2adalah direktori jenis) adalah GNU spesifik.

Anda juga bisa menggunakan linkperintah:

link file1 file2 && rm file1

Namun, jika file1merupakan symlink, tergantung pada implementasinya, file2akan berupa hardlink ke symlink tersebut atau ke target symlink tersebut (pada Solaris, gunakan /usr/sbin/link, bukan /usr/xpg4/bin/link).

Stéphane Chazelas
sumber
2
apakah Anda tahu jika api linux renameat2dengan flag RENAME_NOREPLACEadalah atom?
Fabian Schmitthenner
1
@Fabian, AFAICT memang dimaksudkan untuk tetapi sangat baru dan tidak didukung untuk semua sistem file. Ke depan, kita bisa mengharapkan implementasi mv di Linux untuk menggunakannya nanti. Untuk itulah dirancang.
Stéphane Chazelas
0

Anda juga dapat menggunakan test -e nameyang akan mengembalikan true jika nama ada (terlepas dari file, direktori, atau symlink).

Sebagai contoh:

touch file
mkdir dir
ln -s file symlink
test -e file && echo file exists
test -e dir && echo dir exists
test -e symlink && echo symlink exists
test -e file || echo you wont see this echo
test -e doesnotexist || echo doesnotexist does not exist...
H Briceno
sumber
1
Tapi ln -s doesnotexist exists; test -e exists || echo "does it really not exist?". Sama dengan misalnya ln -s /var/spool/cron/crontabs/. exists(dan Anda bukan root atau anggota grup crontab).
Stéphane Chazelas