mv: ย้ายไฟล์เฉพาะในกรณีที่ไม่มีปลายทาง


44

ฉันสามารถใช้mv file1 file2ในลักษณะที่มันย้ายfile1ไปfile2หากfile2ไม่มีอยู่เท่านั้น?

ฉันได้ลองแล้ว

yes n | mv -i file1 file2

(สิ่งนี้ให้mvถามว่า file2 ควรถูกเขียนทับหรือไม่ตอบโดยอัตโนมัติ) แต่นอกเหนือจากการดูถูก-iมันก็ไม่ได้ให้รหัสข้อผิดพลาดที่ดีกับฉัน (เสมอ 141 แทนที่จะเป็น 0 ถ้าย้ายและอย่างอื่นถ้าไม่ย้าย)


3
คุณต้องมีpipefailตัวเลือกในฐานะ 141 จะออกจากสถานะของyesไม่mvซึ่งจะมีเหตุผลที่จะได้รับ SIGPIPE ที่นี่ไม่มี
Stéphane Chazelas

วิธีการนั้นจะล้มเหลวหาก file2 เป็นไดเรกทอรี (มันจะย้าย file1 ไปยังไดเรกทอรี file2) mv ของ GNU มี-Tไว้สำหรับสิ่งนั้น
Stéphane Chazelas

@ StéphaneChazelasหากความปรารถนาคือการใช้สถานะทางออกmvแทนที่จะyesเป็นทางออกที่ง่ายที่สุดอาจจะเป็นmv -i file1 file2 < <(yes n)
kasperd

คำตอบ:


63

mv -vn file1 file2. คำสั่งนี้จะทำสิ่งที่คุณต้องการ คุณสามารถข้ามได้-vถ้าต้องการ

-v ทำให้เป็น verbose - mv จะบอกคุณว่ามันย้ายไฟล์ถ้ามันย้ายมัน (มีประโยชน์เนื่องจากมีความเป็นไปได้ที่ไฟล์จะไม่ถูกย้าย)

-n ย้ายเฉพาะในกรณีที่ไม่มีไฟล์ 2

โปรดทราบ แต่ที่นี้ไม่ได้เป็น POSIXเป็นที่กล่าวถึงโดย ThomasDickey


2
แต่ก็ไม่ได้POSIX
Thomas Dickey

1
@ThomasDickey POSIX ไม่สนับสนุนสิ่งนี้ด้วยวิธีปรมาณูหรือไม่?
Fabian Schmitthenner

3
ถึง @Fabian: อาจจะไม่ใช่ แต่แม้จะอยู่ในคำตอบที่แนะนำก็อาจมีความเป็นไปได้ที่จะมีการแข่งขันภายในเครื่องมือขึ้นอยู่กับวิธีการเขียน
Thomas Dickey

3
ดูเหมือนว่าจะไม่แข่งฟรีstraceแสดงว่าใช้ (ในระบบของฉัน): stat ("file2", 0x7ffe3e705d10) = -1 ENOENT (ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว) lstat ("file1", {st_mode = S_IFREG | 0644, st_size = 0, ... }) = 0 lstat ("file2", 0x7ffe3e705a10) = -1 ENOENT (ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว) เปลี่ยนชื่อ ("file1", "file2") = 0 lseek (0, 0, SEEK_CUR) = -1 ESPIPE (การค้นหาที่ผิดกฎหมาย) ดังนั้นการเปลี่ยนชื่อจึงถูกนำมาใช้ วิธีแก้ปัญหา @ StéphaneChazelasนั้นน่าจะเหมาะสมถ้าคุณต้องการแข่งแบบฟรี
Fabian Schmitthenner

2
ฉันสงสัยว่าทำไมจึงไม่ใช้renameat2
Fabian Schmitthenner

16

mv -n

จากman mvบนระบบ GNU:

-n, -
ไม่อุดตันไม่เขียนทับไฟล์ที่มีอยู่

บนระบบ FreeBSD:

-nอย่าเขียนทับไฟล์ที่มีอยู่ (ตัวเลือก -n จะแทนที่ตัวเลือก -f หรือ -i ก่อนหน้านี้)


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

หรือ:

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

จะทำงานmvหากfile2ไม่มีอยู่เท่านั้น โปรดทราบว่ามันไม่ได้รับประกันว่าfile2จะไม่ถูกแทนที่เพราะfile2อาจถูกสร้างขึ้นระหว่างการทดสอบและmvแต่โปรดทราบว่าอย่างน้อยรุ่นปัจจุบันของ GNU ที่mvมี-iหรือ-nไม่ให้การรับประกันนั้น (แม้ว่าสภาพการแข่งขันจะแคบกว่า มีตั้งแต่การตรวจสอบเสร็จภายในmv)

ในส่วนอื่น ๆ ก็เป็นแบบพกพาช่วยให้คุณสามารถแยกแยะระหว่างกรณีและทำงานโดยไม่คำนึงถึงชนิดของfile2ไฟล์ (ปกติท่อแม้ไดเรกทอรี )


3
สิ่งนี้นำเสนอสภาวะการแย่งชิงที่ไฟล์สามารถเขียนได้ระหว่างการตรวจสอบการมีอยู่และการย้ายหรือไม่?
Fabian Schmitthenner

3
ความเป็นไปได้เสมอไม่ว่าคุณจะทำอะไร
Majenko

3
Linux API มีrenameat2ซึ่งคุณสามารถให้การRENAME_NOREPLACEตั้งค่าสถานะ ฉันเชื่อว่าอะตอมนี้ตรวจสอบการมีอยู่ของไฟล์แล้วจึงย้ายไฟล์
Fabian Schmitthenner

-d สำหรับไดเรกทอรีหรือ -l สำหรับลิงก์หรือ -e สำหรับไฟล์ทุกประเภท
Majenko

การเปลี่ยนชื่ออาจเป็นการแข่งขันฟรี แต่คำสั่ง mv ที่เหลือไม่ใช่ หากคิดว่าไม่จำเป็นต้องยกเลิกการเชื่อมโยงทันใดการเปลี่ยนชื่อล้มเหลวก็จะเกิดข้อผิดพลาด
Majenko

8

แนวทางการแข่งขันที่ไม่มี GNU ที่lnให้มาfile1นั้นไม่ใช่ไดเรกทอรีประเภท:

ln -PT file1 file2 && rm file1

(ยกเว้นข้อผิดพลาดในระบบไฟล์เครือข่ายบางระบบ) ซึ่งรับประกันได้ว่าจะไม่มีfile2ไฟล์ใดถูกแทนที่ (หรือถ้าfile2เป็นไดเรกทอรีประเภทfile1จะไม่ถูกย้ายเข้าไป) เพราะการlink()โทรของระบบตรงกันข้ามกับการrename()โทรของระบบจะล้มเหลวหาก มีเป้าหมายอยู่

อย่างไรก็ตามจะมีรัฐระดับกลางที่ไฟล์ที่มีอยู่ทั้งในฐานะและfile1file2

-Tตัวเลือก (ที่มักจะทำlink("file1", "file2")แม้ว่าfile2เป็นประเภท directory) เป็น GNU เฉพาะ

คุณสามารถใช้linkคำสั่ง:

link file1 file2 && rm file1

อย่างไรก็ตามหากfile1เป็น symlink ขึ้นอยู่กับการนำไปใช้งานfile2จะเป็นฮาร์ดลิงก์ไปยัง symlink นั้นหรือไปยังเป้าหมายของ symlink นั้น (บน Solaris ให้ใช้/usr/sbin/linkไม่ใช่/usr/xpg4/bin/link)


2
คุณรู้หรือไม่ถ้า linux api ที่renameat2มีค่าสถานะRENAME_NOREPLACEเป็น atomic?
Fabian Schmitthenner

1
@Fabian, AFAICT มันมีความหมาย แต่มันใหม่มากและไม่รองรับระบบไฟล์ทั้งหมด ในอนาคตเราสามารถคาดหวังการใช้งาน mv ในอนาคตบน Linux เพื่อใช้งานได้ นั่นคือสิ่งที่มันถูกออกแบบมาสำหรับ
Stéphane Chazelas

0

คุณสามารถใช้test -e nameสิ่งที่จะคืนค่าจริงหากชื่อมีอยู่ (ไม่ว่าจะเป็นไฟล์ไดเรกทอรีหรือ symlink)

ตัวอย่างเช่น:

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...

1
ln -s doesnotexist exists; test -e exists || echo "does it really not exist?"แต่ เช่นเดียวกันกับln -s /var/spool/cron/crontabs/. exists(และคุณไม่ได้รูทหรือเป็นสมาชิกของกลุ่ม crontab)
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.