การเขียนสคริปต์เชลล์ UNIX: วิธีการย้ายไฟล์ซ้ำในหนึ่งไดเรกทอรี?


8

ฉันมีไฟล์ขนาดเล็กจำนวนมาก f จัดเรียงในโครงสร้างไดเรกทอรีเช่น:

/A/B/C/f

มี 11 ไดเรกทอรีที่ระดับ A แต่ละรายการมีประมาณ 100 ไดเรกทอรีที่ระดับ B แต่ละรายการมีประมาณ 30 ไดเรกทอรีที่ระดับ C แต่ละไดเรกทอรีมีหนึ่งไฟล์ f

ฉันจะย้ายไฟล์ทั้งหมดขึ้นหนึ่งระดับได้อย่างไร ตัวอย่างเช่นกำหนดชุดของไฟล์นี้ ...

/ A / B / C / f1
/ A / B / C / f2
/ A / B / C / f3
/ A / B / C / f4

ฉันต้องการให้ไดเรกทอรี/A/B/มี 4 ไฟล์, f1 ถึง f4 ไม่จำเป็นต้องลบไดเรกทอรี C

ฉันหวังว่าปัญหานี้เป็นปัญหาแก้ไขได้อาจจะเกี่ยวข้องกับการfind, และxargs whatnotความคิดใด ๆ

ไชโย

เจมส์

คำตอบ:


9

มันค่อนข้างง่ายด้วย GNU find (เท่าที่พบบน Linux) หรือการค้นหาอื่น ๆ ที่สนับสนุน-execdir:

find A -type f -execdir mv -i {} .. \;

ด้วยมาตรฐานfind:

find A -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

ด้วย zsh:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

หากโครงสร้างไดเรกทอรีมีระดับการซ้อนเหมือนกันคุณไม่จำเป็นต้องมีการวนซ้ำแบบซ้ำ (แต่ลบไดเรกทอรีที่ว่างเปล่าออกก่อน):

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done

1

สำหรับชุดของไฟล์สิ่งนี้จะทำ:

$ cd /A/B/C/
$ mv ./* ../

แต่ฉันคาดหวังว่าปัญหาของคุณจะค่อนข้างซับซ้อน ... ฉันไม่สามารถตอบคำถามนี้ได้ ... ฉันไม่แน่ใจว่าโครงสร้าง dir ของคุณคือ ... คุณช่วยชี้แจงได้ไหม?


สิ่งนี้จะทำงานได้ดีกับไดเรกทอรีใดไดเรกทอรีหนึ่ง / A / B / C แต่เมื่อฉันมีไดเรกทอรีดังกล่าวประมาณ 30,000 รายการฉันหวังว่าจะมีคำสั่งที่สามารถดำเนินการที่ด้านบนสุดของลำดับชั้นเพื่อดูแลพวกเขาทั้งหมดในครั้งเดียว
jl6

1
@ jl6 ฉันเห็นและไฟล์เท่านั้นที่ต้องการย้าย? ดังนั้น C ไม่จำเป็นต้องย้ายไปที่ B และ B ไม่ใช่ A ฯลฯ ...
Stack Overflow นั้นตายแล้ว

ถูกต้อง - การดำเนินการควรดำเนินการกับไฟล์ที่ระดับต่ำสุดของลำดับชั้นเท่านั้นและควรดำเนินการในระดับหนึ่งขึ้นไป
jl6

0

เดาแรกของฉันคือ

$ find A -type f -exec mv {} .. \;  

ตราบใดที่คุณไม่ได้ระบุ-depthมันควรจะโอเค ฉันไม่ได้ลองใช้ดังนั้นให้ทดสอบก่อนที่คุณจะยอมรับ


ค้นหาจะทำอย่างไรเมื่อมีสองไฟล์ที่มีชื่อเหมือนกัน?

ค้นหาไม่ใช่ปัญหาคำสั่ง "mv" ที่ฝังอยู่คือ ชื่อที่ซ้ำกันจะไม่ดีเนื่องจาก mv จะไม่เปลี่ยนชื่อ แต่จะเขียนทับ คุณสามารถใช้ mv -i เพื่อช่วยเล็กน้อย แต่มีไฟล์มากกว่า 30,000 ไฟล์ที่อาจเจ็บปวด หากคุณต้องการควบคุมสถานการณ์คุณอาจต้องใช้โปรแกรมที่เขียนด้วยภาษาสคริปต์ (ตัวเลือกของฉันคือ python, YMMV)
hotei

ฉันไม่ได้ใช้ "backups" กับ mv แต่การดูหน้า man ดูเหมือนว่าอาจเป็นวิธีแก้ปัญหาชื่อ dupe ของคุณ "ค้นหา A-type f -exec mv - แบ็คอัพหมายเลข {} .. \;" อาจทำงานได้ (โปรดทราบว่านั่นคือเส้นประสองครั้งใน
แบ็คอัพ

ตัวอย่างการค้นหาที่ระบุจะย้ายไฟล์ทั้งหมดที่ระดับต่ำสุดไปยังไดเรกทอรีที่อยู่เหนือ A (ฉันเชื่อว่า ".. " กำลังถูกตีความโดยเชลล์ก่อนที่จะถูกส่งผ่านไปยัง / mv?) ฉันต้องการไฟล์ที่จะย้ายเพียงหนึ่งระดับขึ้นไปในลำดับชั้น การชี้แจงเพิ่มเติมอีกข้อหนึ่ง: จะไม่มีชื่อไฟล์ซ้ำตราบใดที่ไฟล์นั้นเลื่อนระดับขึ้นหนึ่งระดับเท่านั้น
jl6

@hotei: mvคำสั่งถูกดำเนินการในไดเรกทอรีปัจจุบันดังนั้น..อย่าทำสิ่งที่คุณหวัง ดูคำตอบของฉันสำหรับการแก้ปัญหา @ jl6: เชลล์ไม่มีส่วนเกี่ยวข้องกับ..การจัดการโดยเคอร์เนล
Gilles 'หยุดความชั่วร้าย'

0

หากคุณต้องการย้ายไฟล์ที่อยู่ในไดเรกทอรีใบไม้ (เช่นคุณไม่ต้องการย้าย/A/B/fileไปที่/AหากBมีไดเรกทอรีย่อย) ต่อไปนี้เป็นวิธีที่ทำได้สองวิธี:

ทั้งสองต้องการสิ่งนี้

leaf ()
{
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
}
shopt -s nullglob

อันนี้ใช้ได้ผล:

leaf A | while read -r dir
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

จะเร็วกว่านี้ แต่ไม่ชอบไดเรกทอรีแหล่งที่ว่างเปล่า:

leaf A | while read -r dir
do
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"
done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.