กำลังผสานโฟลเดอร์ด้วย mv หรือไม่


149

หากฉันใช้mvเพื่อย้ายโฟลเดอร์ชื่อ "โฟลเดอร์" ไปยังไดเรกทอรีที่มีอยู่แล้ว "โฟลเดอร์" พวกเขาจะรวมหรือจะถูกแทนที่หรือไม่

คำตอบ:


120

mvไม่สามารถรวมหรือเขียนทับไดเรกทอรีมันจะล้มเหลวด้วยข้อความ"mv: ไม่สามารถย้าย 'a' เป็น 'b': ไดเรกทอรีไม่ว่าง"แม้ว่าคุณจะใช้--forceตัวเลือกก็ตาม


คุณสามารถทำงานรอบนี้โดยใช้เครื่องมืออื่น ๆ (เช่นrsync, findหรือแม้กระทั่งcp) แต่คุณต้องพิจารณาอย่างรอบคอบความหมาย:

  • rsyncสามารถรวมเนื้อหาของไดเรกทอรีหนึ่งเข้ากับอีกไดเรกทอรีหนึ่ง (โดยมีตัวเลือก--remove-source-files1ตัวเลือกเพื่อลบไฟล์ต้นฉบับเหล่านั้นที่ถ่ายโอนได้อย่างปลอดภัยและด้วยตัวเลือกการสงวนสิทธิ์ / ความเป็นเจ้าของ / เวลาปกติ-aหากคุณต้องการ)
    ... แต่นี่เป็นการดำเนินการคัดลอกแบบสมบูรณ์ และอาจเป็นดิสก์มาก
  • ตัวเลือกที่ต้องการขณะนี้:คุณสามารถรวมrsyncของ--link-dest=DIRตัวเลือก (การสร้าง hardlinks แทนการคัดลอกเนื้อหาของไฟล์ที่เป็นไปได้) และจะได้รับความหมายคล้ายกับปกติ--remove-source-files สำหรับสิ่งนี้จะต้องได้รับเส้นทางที่แน่นอนไปยังไดเรกทอรีแหล่งที่มา (หรือเส้นทางสัมพัทธ์จากปลายทางไปยังแหล่งที่มา ) … แต่สิ่งนี้ใช้ในลักษณะที่ไม่ได้ตั้งใจ (ซึ่งอาจหรืออาจไม่ก่อให้เกิดภาวะแทรกซ้อน) ต้องรู้ (หรือกำหนด) พา ธ สัมบูรณ์ไปยังแหล่งที่มา (เพื่อเป็นข้อโต้แย้ง) และปล่อยให้โครงสร้างไดเรกทอรีว่างเปล่าหายไป ต่อ1mv
    --link-dest
    --link-dest--link-dest
  • คุณสามารถใช้findเพื่อสร้างโครงสร้างไดเรกทอรีต้นฉบับใหม่ที่เป้าหมายจากนั้นย้ายแต่ละไฟล์ตามจริง
    แต่จะต้องเรียกคืนผ่านแหล่งข้อมูลหลายครั้งและสามารถพบกับสภาพการแข่งขัน (ไดเรกทอรีใหม่ที่ถูกสร้างขึ้นที่ต้นทางในระหว่างกระบวนการหลายขั้นตอน )
  • cpสามารถสร้างฮาร์ดลิงก์ (ใส่ตัวชี้เพิ่มเติมไปยังไฟล์ที่มีอยู่เดิม) ซึ่งสร้างผลลัพธ์คล้ายกับการผสานmv(และมีประสิทธิภาพ IO มากเนื่องจากมีการสร้างตัวชี้เท่านั้นและไม่มีการคัดลอกข้อมูลจริง)
    ... แต่สิ่งนี้ อีกครั้งทนทุกข์ทรมานจากสภาพการแข่งขันที่เป็นไปได้ (ไฟล์ใหม่ที่แหล่งที่ถูกลบแม้ว่าพวกเขาจะไม่ได้คัดลอกในขั้นตอนก่อนหน้า)

วิธีแก้ปัญหาใดที่เหมาะสม (ขึ้นอยู่กับความเหมาะสม) นั้นขึ้นอยู่กับกรณีการใช้งานเฉพาะของคุณ
เช่นเคยคิดก่อนที่คุณจะดำเนินการคำสั่งเหล่านี้และมีการสำรอง


1: โปรดทราบว่าrsync --remove-source-filesจะไม่ลบไดเรกทอรีใด ๆ ดังนั้นคุณจะต้องทำสิ่งfind -depth -type d -empty -deleteต่อไปนี้เพื่อกำจัดทรีไดเรกทอรีว่างเปล่า


ดูเหมือนคุณเพิ่งลองใช้งานไปแล้วหนึ่งmvครั้ง คำตอบนี้จะดีขึ้นด้วยความจริงที่กว้างขึ้น Linux, BSD และ Unix "ของจริง" หรืออ้างอิงจาก POSIX หรือ SUS
Warren Young

@WarrenYoung ถูกต้องฉันพยายามทดลองmvใช้งานโดย Debian เท่านั้น - การเน้นที่พยายามเนื่องจาก manpage ไม่ได้พูดถึงพฤติกรรมนี้ ...
n.st

38
ข้อเสียของ rsync คือการคัดลอกข้อมูลแทนที่จะเปลี่ยนฮาร์ดลิงก์ซึ่งเป็นทรัพยากรที่มีความเข้มข้นสูงหากคุณต้องรับมือกับข้อมูลจำนวนมาก
Jonathan Mayer

7
@Keith โปรดทราบว่า--deleteจะลบเฉพาะไฟล์ในไดเรกทอรีปลายทางที่ไม่มีอยู่ในไดเรกทอรีต้นทาง
n.st

1
@JonathanMayer rsync เป็นคุณสมบัติเชื่อมโยงหลายอย่างที่เกี่ยวข้อง ตัวอย่างเช่นคุณสามารถรักษาความเชื่อมโยงอย่างหนักกับ-Hฟังก์ชั่นหรือคุณสามารถ hardlink --link-destไฟล์ในรูปแบบต่างๆโดยใช้ ดูหน้าคนก่อนที่จะใช้พวกเขาแม้ว่า
อัลโล

87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/

สิ่งนี้จะลบไฟล์ต้นฉบับในข้อคิดเห็นโดย n.st หรือไม่
Dominique

3
ไม่ฉันต้องการทำสองขั้นตอนด้วยเหตุผลด้านความปลอดภัย แหล่งที่รวมและลบออกไม่สามารถย้อนกลับได้ ขั้นตอนการเพิ่มเข้าใน n.st anwer เป็นสิ่งจำเป็นเช่นกัน (เพื่อลบไดเรกทอรี)
fazie

3
--remove-source-filesมีข้อดีของการลบไฟล์ที่ถ่ายโอนสำเร็จเท่านั้นดังนั้นคุณสามารถใช้findเพื่อลบไดเรกทอรีที่ว่างเปล่าและจะถูกทิ้งไว้กับทุกสิ่งที่ไม่ได้ถ่ายโอนโดยไม่ต้องตรวจสอบrsyncเอาต์พุต
n.st

4
แต่มันไม่ได้เคลื่อนที่จริง ๆ แล้วความเร็วในการกระแทกนั้นสูงมากหากไฟล์ขนาดใหญ่เข้ามาเกี่ยวข้อง
Alex

แต่คุณไม่สามารถทำการเคลื่อนไหวที่แท้จริงและการผสานอย่างแท้จริง
fazie

64

คุณสามารถใช้-lตัวเลือกของคำสั่งcpซึ่งสร้างการเชื่อมโยงอย่างหนักของไฟล์ในระบบไฟล์เดียวกันแทนการคัดลอกข้อมูลแบบเต็ม คำสั่งต่อไปนี้คัดลอกโฟลเดอร์source/folderไปยังโฟลเดอร์หลัก ( destination) ซึ่งมีไดเรกทอรีที่มีชื่อfolderอยู่แล้ว

cp -rl source/folder destination
rm -r source/folder

คุณอาจต้องการใช้-P( --no-dereference- อย่ายกเลิกการเชื่อมโยงสัญลักษณ์อ้างอิง) หรือ-a( --archive- สงวนข้อมูลเมตาทั้งหมดรวมถึง-Pตัวเลือก) ขึ้นอยู่กับความต้องการของคุณ


7
@rautamiekka: ฉันคิดว่าคุณกำลังถามเหตุผลที่ใช้ฮาร์ดลิงก์ หากคุณไม่ทราบว่าฮาร์ดลิงก์คืออะไรและทำไมคุณควรใช้ลิงก์เหล่านั้นคุณอาจไม่ควรใช้เส้นทางนี้ อย่างไรก็ตามการสร้างฮาร์ดลิงก์ไม่ได้ทำสำเนาแบบเต็มดังนั้นการดำเนินการนี้จะใช้เวลาน้อยกว่าการคัดลอกแบบเต็ม และคุณจะใช้ฮาร์ดลิงก์แทนซอฟต์ลิงก์เพื่อให้คุณสามารถลบไฟล์ต้นฉบับและยังมีข้อมูลที่ถูกต้องแทนพอยน์เตอร์ไปยังพา ธ ที่ไม่ถูกต้อง และcpแทนที่จะเป็นrsyncเพราะทุกระบบมีcpและทุกคนมีความคุ้นเคยกับมัน
palswim

7
ความสามารถของโซลูชันนี้อาจถูกมองข้ามว่าไม่ใช่คำตอบที่ยอมรับได้ มันเป็นทางออกที่สง่างาม คุณจะได้รับความสามารถในการผสานด้วยเวลาการดำเนินงานของcp mv
TheHerk

2
หากคุณรู้ว่าคุณไม่จำเป็นต้องย้ายไฟล์ที่มีอยู่แล้วในปลายทางคุณต้องการเพิ่มด้วย-n
2560

1
@Ruslan: นี่เป็นความจริง แต่คุณไม่สามารถย้ายได้หากไม่มีการคัดลอกข้ามระบบไฟล์ด้วยวิธีการใด ๆ แม้mv /fs1/file /fs2/(ข้ามระบบไฟล์) จะทำการคัดลอกและลบ
palswim

2
ถูกต้อง แต่ในขณะที่mvจะใช้งานได้ (หาก dir เป้าหมายยังไม่มีอยู่) แม้ว่าจะไม่ "มีประสิทธิภาพ" หรือสิ่งที่คุณเรียกมันcp -rlจะล้มเหลว
Ruslan

23

ฉันขอแนะนำสี่ขั้นตอนเหล่านี้:

cd ${SOURCE}; 
find . -type d -exec mkdir -p ${DEST}/\{} \; 
find . -type f -exec mv \{} ${DEST}/\{} \; 
find . -type d -empty -delete

หรือดีกว่ายังนี่เป็นสคริปต์ที่ใช้ความหมายคล้ายกับmv:

#!/bin/bash

DEST="${@:${#@}}"
ABS_DEST="$(cd "$(dirname "$DEST")"; pwd)/$(basename "$DEST")"

for SRC in ${@:1:$((${#@} -1))}; do   (
    cd "$SRC";
    find . -type d -exec mkdir -p "${ABS_DEST}"/\{} \;
    find . -type f -exec mv \{} "${ABS_DEST}"/\{} \;
    find . -type d -empty -delete
) done

Args คือ SOURCE, DEST
schuess

มันดูมีประโยชน์ทีเดียว ฉันอยากจะใช้มันเพื่อล้างฮาร์ดไดรฟ์ของฉัน ผู้เชี่ยวชาญรายอื่นสามารถแสดงความคิดเห็นก่อนที่ฉันจะมอบหมายสำเนาสำรองให้สคริปต์นี้หรือไม่ :-)
LarsH

BTW หากคุณต้องการทำเทียบเท่าrsync -u(อัปเดตเฉพาะในกรณีที่ใหม่กว่า) mv(ในบางรุ่นอย่างน้อย) สามารถใช้-uตัวเลือก อย่างไรก็ตามในกรณีดังกล่าวคุณอาจต้องการลบไดเรกทอรีต้นทางที่ไม่ว่างเปล่ารวมถึงไดเรกทอรีว่างเปล่าเพื่อให้ครอบคลุมกรณีที่ไฟล์ในแผนผังต้นไม้ไม่ใหม่ @schuess: ดูเหมือนว่าอาจมีอาร์กิวเมนต์ SOURCE หลายรายการหากคุณต้องการ
LarsH

1
สิ่งนี้ไม่สามารถจัดการกับช่องว่างได้ดี ฉันลองมันกับบางไดเรกทอรีที่มีช่องว่างในนั้นและจบลงด้วยชุดซ้อนของไดเรกทอรีที่ไม่สิ้นสุด
rofer

16

นี่คือวิธีที่จะรวมไดเรกทอรี มันเร็วกว่า rsync เพียงแค่เปลี่ยนชื่อไฟล์แทนการคัดลอกแล้วลบทิ้ง

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'

นั่นเป็นเรื่องที่น่าสนใจ แต่มีความเกี่ยวข้องกับหัวข้ออย่างคลุมเครือและไม่ได้พูดถึงสิ่งที่ผู้ใช้ถามจากระยะไกล
Shadur

17
ที่จริงแล้วโค้ดของ Jewel ทำในสิ่งที่ผู้ใช้ร้องขออย่างแน่นอนยกเว้นการสร้างไดเรกทอรีที่ขาดหายไป บางทีคุณควรจะดูอีกครั้ง?
Jonathan Mayer

3
ฉันจะเพิ่มเพื่อใช้ "-print0" ในการค้นหาและ "-0" ใน xargs เพราะมีไฟล์ที่มีช่องว่างในชื่อ นอกจากนี้ยังมีปัญหาเล็ก ๆ หากชื่อมีวงเล็บพวกเขาจะไม่ถูกย้าย
markuz

2
นี่เร็วกว่า rsync สำหรับไฟล์จำนวนน้อย แต่มันก็เป็นกระบวนการใหม่สำหรับไฟล์ทุกไฟล์ดังนั้นประสิทธิภาพจึงน่าทึ่งด้วยไฟล์ขนาดเล็กจำนวนมาก @ คำตอบของ palswim ไม่ประสบปัญหานี้
b0fh

2
คำสั่งจะล้มเหลวหากในdestนั้นเป็นไดเรกทอรีที่มีชื่อเดียวกันกับในsourceแล้ว และไฟล์จะถูกย้ายไปยังที่อยู่ในdest sourceคำสั่งไม่ทำอะไรเลยมากกว่าmv source/* source/dest/.
ceving

3

วิธีหนึ่งที่จะทำให้สิ่งนี้สำเร็จคือใช้:

mv folder/* directory/folder/
rmdir folder

ตราบใดที่ไม่มีไฟล์สองไฟล์ที่มีชื่อเหมือนกันfolderและdirectory/folderคุณจะได้ผลลัพธ์เช่นการผสาน


3
วิธีการว่าจะrm folderทำงานอย่างไร
JakeGould

5
@ JakeGould ไม่ได้เลย :)
n.st

rm folder -fRใช้ได้กับฉันเสมอ
Octopus

2
โปรดระวังว่าสิ่งนี้จะไม่ทำงานกับไฟล์ที่ซ่อนอยู่
b0fh

2

สำหรับสำเนาที่บริสุทธิ์ที่สุดฉันใช้วิธีคัดลอก tar (-) B blockread

ตัวอย่างจากภายในพา ธ ต้นทาง ('cd' ที่นั่นหากจำเป็น):

tar cBf - <sourcefolder> | (cd /your/target/folder ; tar xBf -)

สิ่งนี้จะสร้างสำเนาต้นไม้ต้นกำเนิดที่แน่นอนโดยที่เจ้าของและสิทธิ์อนุญาตยังคงเหมือนเดิม และหากมีโฟลเดอร์เป้าหมายข้อมูลจะถูกรวมเข้าด้วยกัน เฉพาะไฟล์ที่มีอยู่แล้วเท่านั้นที่จะถูกเขียนทับ

ตัวอย่าง:

 $ cd /data1/home
 $ tar cBf - jdoe | (cd /data2/home ; tar xBf -)

เมื่อการคัดลอกเสร็จสมบูรณ์คุณสามารถลบแหล่งที่มา ( rm -rf <source>) แน่นอนว่านี่ไม่ใช่การย้ายที่แน่นอน: ข้อมูลจะถูกคัดลอกจนกว่าคุณจะลบแหล่งที่มา

ในฐานะที่เป็นตัวเลือกที่คุณสามารถ verbose (แสดงบนหน้าจอไฟล์ที่ถูกคัดลอก) ด้วย -v: tar cBvf -

  • c: สร้าง
  • B: read full block (สำหรับไปป์ read)
  • v: verbose
  • f: ไฟล์ที่จะเขียน
  • x: สารสกัด
  • -: stdout / stdin

sourcefolderสามารถเป็นได้*(สำหรับทุกอย่างในโฟลเดอร์ปัจจุบัน)


การระบุf -tar โดยปกติไม่จำเป็น - ค่าดีฟอลต์คืออ่านจาก stdin / write to stdout
muru

1

นี่คือสคริปต์ที่เหมาะกับฉัน ฉันชอบ mv มากกว่า rsync ดังนั้นฉันจึงใช้โซลูชั่นของ Jewel และ Jonathan Mayer

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd $SRC;
    find . -type d -exec mkdir -p ${DEST}/{} \;
    find . -type f -exec mv {} ${DEST}/{} \;
    find . -type d -empty -delete
    popd
done

วิธีการแก้ปัญหานี้ไม่ได้รับการยกเว้นชื่อเส้นทางอย่างถูกต้องระวัง
user12439

@ user12439 ฉันจะอัปเดตโซลูชันถ้าคุณแสดงให้ฉันส่วนที่จะแก้ไข
xer0x

1

ไม่ใช่ความคิดที่ดีที่จะใช้คำสั่งเช่น cp หรือ rsync สำหรับไฟล์ขนาดใหญ่มันจะใช้เวลานาน mv เร็วกว่ามากเพราะมันอัพเดตเฉพาะ inodes โดยไม่ทำการคัดลอกไฟล์ทางกายภาพ ตัวเลือกที่ดีกว่าคือการใช้ตัวจัดการไฟล์ของระบบปฏิบัติการของคุณ สำหรับ Opensuse จะมีโปรแกรมจัดการไฟล์ชื่อ Konquerer มันสามารถย้ายไฟล์ได้โดยไม่ต้องคัดลอกไฟล์จริง มันมีฟังก์ชั่น "ตัดและวาง" เหมือนใน Windows เพียงเลือกไดเรกทอรีย่อยทั้งหมดในไดเรกทอรี A. คลิกขวาและ "ย้ายเข้าไปใน" ไดเรกทอรี B ซึ่งอาจมีไดเรกทอรีย่อยที่มีชื่อเดียวกัน มันจะรวมพวกเขา นอกจากนี้ยังมีตัวเลือกว่าคุณต้องการเขียนทับหรือเปลี่ยนชื่อไฟล์ด้วยชื่อเดียวกัน


1
OP ถามว่าเกิดอะไรขึ้นเมื่อmvมีการใช้งาน
don_crissti

0

Python solution

เนื่องจากฉันไม่สามารถหาวิธีแก้ไขปัญหาที่มีอยู่แล้วที่น่าพอใจฉันตัดสินใจที่จะสร้างสคริปต์ Python อย่างรวดเร็วเพื่อให้บรรลุ

โดยเฉพาะอย่างยิ่งวิธีการนี้มีประสิทธิภาพเพราะจะเดินทรีไฟล์ต้นฉบับเพียงครั้งเดียวเท่านั้น

นอกจากนี้ยังช่วยให้คุณปรับแต่งสิ่งต่างๆอย่างรวดเร็วเช่นเขียนทับไฟล์ที่คุณต้องการ

การใช้งาน:

move-merge-dirs src/ dest/

จะย้ายเนื้อหาทั้งหมดของsrc/*ไปยังdest/และsrc/จะหายไป

ย้ายผสาน-dirs

#!/usr/bin/env python3

import argparse
import os

def move_merge_dirs(source_root, dest_root):
    for path, dirs, files in os.walk(source_root, topdown=False):
        dest_dir = os.path.join(
            dest_root,
            os.path.relpath(path, source_root)
        )
        if not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        for filename in files:
            os.rename(
                os.path.join(path, filename),
                os.path.join(dest_dir, filename)
            )
        for dirname in dirs:
            os.rmdir(os.path.join(path, dirname))
    os.rmdir(source_root)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Move merge src/* into dest. Overwrite existing files.'
    )
    parser.add_argument('src_dir')
    parser.add_argument('dest_dir')
    args = parser.parse_args()
    move_merge_dirs(args.src_dir, args.dest_dir)

GitHub ต้นน้ำ

ดูเพิ่มเติมที่: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command


0

นี่คือคำสั่งสำหรับการย้ายไฟล์และโฟลเดอร์ไปยังปลายทางอื่น:

$ mv /source/path/folder /target/destination/

โปรดจำไว้ว่า : mvคำสั่งจะไม่ทำงานหากโฟลเดอร์จะถูกรวมเข้าด้วยกัน (เช่นโฟลเดอร์อื่นที่มีชื่อเดียวกันอยู่แล้วในปลายทาง) และปลายทางไม่ว่างเปล่า

mv: ไม่สามารถย้าย '/ source / path / folder' เป็น '/ target / destination / folder': ไดเรกทอรีไม่ว่างเปล่า

หากโฟลเดอร์ปลายทางว่างเปล่าคำสั่งด้านบนจะทำงานได้ดี

ดังนั้นในการรวมทั้งสองโฟลเดอร์เข้าด้วยกัน
ไม่ว่าจะในกรณีใดให้ทำใน 2 คำสั่ง:

$ cp -rf /source/path/folder /target/destination/
$ rm -rf /source/path/folder

หรือรวมทั้งสองเป็นคำสั่งแบบครั้งเดียว:

$ cp -rf /source/path/folder /target/destination/ && rm -rf /source/path/folder

mv = move
cp = copy
rm = remove

-rสำหรับไดเรกทอรี (โฟลเดอร์)
-f การบังคับให้ทำงาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.