แบนไดเรกทอรีที่ซ้อนกัน


71

นี่อาจจะง่ายมาก แต่ฉันไม่สามารถเข้าใจได้ ฉันมีโครงสร้างไดเรกทอรีเช่นนี้ (dir2 อยู่ภายใน dir1):

/dir1
    /dir2 
       |
        --- file1
       |
        --- file2

เป็นวิธีที่ดีที่สุดในการ 'แผ่' โครงสร้างผู้กำกับนี้ในลักษณะที่จะได้รับ file1 และ file2 ใน dir1 ไม่ใช่ dir2


คำตอบ:


75

คุณสามารถทำได้ด้วย GNU findและ GNU mv:

find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +

โดยทั่วไปแล้ววิธีการทำงานหากfindผ่านไดเรคทอรีทั้งไดเรกทอรีและสำหรับแต่ละไฟล์ ( -type f) ที่ไม่ได้อยู่ในไดเรกทอรีระดับบนสุด ( -mindepth 2) จะรัน a mvเพื่อย้ายไปยังไดเรกทอรีที่คุณต้องการ ( -exec mv … +) -tอาร์กิวเมนต์mvช่วยให้คุณระบุไดเรกทอรีปลายทางแรกซึ่งเป็นสิ่งจำเป็นเพราะ+รูปแบบของการ-execทำให้ทุกสถานที่แหล่งที่มาในตอนท้ายของคำสั่ง -iยี่ห้อmvถามก่อนที่จะเขียนทับซ้ำกันใด ๆ คุณสามารถแทนที่-fเขียนทับพวกเขาโดยไม่ถาม (หรือ-nไม่ถามหรือเขียนทับ)

ดังที่ Stephane Chazelas ชี้ให้เห็นข้างต้นใช้งานได้กับเครื่องมือ GNU เท่านั้น (ซึ่งเป็นมาตรฐานบน Linux แต่ไม่ใช่ระบบอื่น ๆ ส่วนใหญ่) ต่อไปนี้ค่อนข้างช้า (เพราะเรียกmvหลาย ๆ ครั้ง) แต่มีความเป็นสากลมากกว่า:

find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'

3
แก้ไขเพื่อใช้-exec +เพื่อไม่ให้ประมวลผลเป็นจำนวนมากmv
Random832

1
@ Random832 และจะย้อนกลับอีกครั้งเนื่องจาก + ไม่ทำงาน mvต้องการปลายทางเป็นอาร์กิวเมนต์สุดท้าย แต่ + จะมีแหล่งที่มาเป็นอาร์กิวเมนต์สุดท้าย ค้นหาจะไม่แม้แต่ยอมรับไวยากรณ์ที่คุณเปลี่ยนเป็น ( find: missing argument to `-exec')
derobert

1
@ Random832 แต่ฉันคิดว่าmvมี-tเราสามารถใช้ดังนั้นฉันจะเปลี่ยนเป็น
Derobert

1
@Dom findพิมพ์ไฟล์ที่ซ่อนอยู่ (จุด) ตามค่าเริ่มต้น ความลึกนั้นสัมพันธ์กับไดเรกทอรีที่คุณผ่านการค้นหา
Derobert

1
หรือfind ./dir -mindepth 2 -type f -exec mv -f '{}' ./dir ';'ถ้าแทนที่ซ้ำซ้อน
Atav32

33

ใน zsh:

mv dir1/*/**/*(.D) dir1

**/สำรวจเส้นทางไดเรกทอรีย่อยซ้ำ ตัวระบุคุณสมบัติกลม .จับคู่ไฟล์ปกติเท่านั้นและDทำให้แน่ใจว่ามีไฟล์ dot รวมอยู่ (โดยค่าเริ่มต้นไฟล์ที่ชื่อขึ้นต้นด้วย a .จะถูกแยกออกจากการจับคู่สัญลักษณ์แทน) ในการทำความสะอาดไดเรกทอรีตอนนี้ว่างเปล่าหลังจากนั้นวิ่งrmdir dir1/**/*(/Dod)- /จำกัด ไปยังไดเรกทอรีและodสั่งลึกการแข่งขันครั้งแรกเพื่อให้เป็นไปลบก่อนdir1/dir2/dir3dir1/dir2

หากความยาวทั้งหมดของชื่อไฟล์มีขนาดใหญ่มากคุณอาจพบข้อ จำกัด เกี่ยวกับความยาวบรรทัดคำสั่ง Zsh มี builtins สำหรับmvและrmdirไม่ได้รับผลกระทบจากข้อ จำกัด นี้: เรียกใช้zmodload zsh/filesเพื่อเปิดใช้งาน

ด้วยเครื่องมือ POSIX เท่านั้น:

find dir1 -type f -exec mv {} dir1 \;
find dir1 -depth -exec rmdir {} \;

หรือ (เร็วขึ้นเพราะไม่จำเป็นต้องเรียกใช้กระบวนการแยกต่างหากสำหรับแต่ละไฟล์)

find dir1 -type f -exec sh -c 'mv "$@" dir1' _ {} +
find dir1 -depth -exec rmdir {} +

1
นี่ควรเป็นคำตอบที่ยอมรับได้! โดยเฉพาะอย่างยิ่งกับรุ่น zsh ที่รัดกุม
Adamski

3

ลองทำสิ่งนี้:

cp /dir1/dir2/file{1,2} /another/place

หรือสำหรับแต่ละไฟล์ที่ตรงกันfile[0-9]*ในส่วนย่อย:

cp /dir1/dir2/file[0-9]* /another/place

ดูhttp://mywiki.wooledge.org/glob


ฉันควรจะระบุสิ่งนี้ แต่ฉันต้องใช้ไฟล์จำนวนมากเพื่อใช้{}ในปัญหาที่แท้จริงของฉัน
เต่า

ดูโซลูชันที่สองของฉัน
Gilles Quenot

การเล่นชนิดหนึ่ง ขอบคุณสำหรับความช่วยเหลือ นี่เป็นทางออกที่ดีที่สุดอย่างแน่นอน
เต่า

2

ฉันเขียนสองฟังก์ชันที่คุณสามารถใช้ร่วมกันเพื่อทำเช่นนั้นคุณสามารถ จำกัด ระดับไดเรกทอรีได้โดยการเพิ่ม-maxdepth $VALพารามิเตอร์

# This scripts flattens the file directory
# Run this script with a folder as parameter:
# $ path/to/script path/to/folder

#!/bin/bash

rmEmptyDirs(){
    local DIR="$1"
    for dir in "$DIR"/*/
    do
        [ -d "${dir}" ] || continue # if not a directory, skip
        dir=${dir%*/}
        if [ "$(ls -A "$dir")" ]; then
            rmEmptyDirs "$dir"
        else
            rmdir "$dir"
        fi
    done
    if [ "$(ls -A "$DIR")" ]; then
        rmEmptyDirs "$DIR"
    fi
}

flattenDir(){
    local DIR="$1"
    find "$DIR" -mindepth 2 -type f -exec mv -i '{}' "$DIR" ';'
}

read -p "Do you wish to flatten folder: ${1}? " -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    flattenDir "$1" &
    rmEmptyDirs "$1" &
    echo "Done";
fi

ชายฉันเพิ่งใช้สคริปต์ของคุณในทางที่ผิดโดยลืมอาร์กิวเมนต์พา ธ ที่ทำให้เซิร์ฟเวอร์ของฉันสับสนจริงๆ ตกลงฉันคนที่คัดลอกวางและสิ่งที่ผิดพวกเขา แต่พวกฉลาดและเพิ่มการตรวจสอบ / ยืนยันกับสคริปต์ที่ลบ / ย้ายวัตถุดิบเช่นนั้น ...
dulgan

อ๊ะ! ฉันเสียใจที่ได้ยินเช่นนั้น หวังว่าคุณจะมีการสำรองข้อมูล ... ฉันได้เพิ่มการยืนยันเพื่อการป้องกันในอนาคต
บรูโน่

ขอบคุณ @Bruno ที่ดีกว่านี้มาก เซิร์ฟเวอร์ของฉันยังคงทำงานอย่างไม่มีที่ติฉันแสดงความคิดเห็นในส่วน "แบน" เพื่อลบไดเรกทอรีว่างเปล่าซ้ำ ๆ (และนั่นเป็นข้อผิดพลาดของฉัน) รากจนกว่าฉันจะเห็นข้อผิดพลาดที่ทำให้ฉันหยุดการทำงานของสคริปต์
dulgan

1

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

dir1/
├── dir2
   └── file
└── dir3
    └── file

ในกรณีนี้ตัวเลือก-i( --interactive) ที่ส่งไปยังmvจะไม่ให้ผลลัพธ์ที่ต้องการเพื่อทำให้โครงสร้างไดเรกทอรีเรียบและจัดการความขัดแย้งของชื่อ ดังนั้นมันจึงถูกแทนที่ด้วย--backup=t(เทียบเท่า--backup=numbered) เอกสารเพิ่มเติมเกี่ยวกับ-b( --backup) ตัวเลือกที่มีอยู่ในhttps://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options

ที่เกิดขึ้นใน:

find dir1/ -mindepth 2 -type f -exec mv -t dir1/ --backup=t '{}' +

ซึ่งให้ผลผลิต:

dir1/
├── dir2
├── dir3
├── file
└── file.~1~

1

tar และ zip ทั้งสองมีความสามารถในการรวมและแยกโครงสร้างไดเรกทอรีออกไปดังนั้นฉันจึงสามารถยุบไดเรกทอรีที่ซ้อนกันได้อย่างรวดเร็วด้วย

tar -cvf all.tar *

ตามด้วยการย้าย all.tar ไปยังตำแหน่งใหม่แล้ว

tar -xvf all.tar --strip=4

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