ไฟล์ 'เขียนทับ' พื้นที่ว่างยังคงหายไปหรือไม่


11

ดังนั้นคนใจร้อนใบ้ฉันจึงใช้สคริปต์ต่อไปนี้บนเซิร์ฟเวอร์ 19.04 ของฉันในความพยายามที่จะย้ายไฟล์วิดีโอไปไว้ในโฟลเดอร์ที่มีคำนำหน้า:

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
shopt -s nocasematch

for file in *
do
    for dir in "${dirs[@]}"
    do

     if [ -d "$file" ]; then
      echo 'this is a dir, skipping'
      break
     else
      if [[ $file =~ ^[$dir] ]]; then
       echo "----> $file moves into -> $dir <----"
       mv "$file" "$dir"
       break
      fi
     fi
  done
done

ไม่มีเบาะแสที่ผิดพลาด แต่แทนที่จะย้ายไฟล์ไปยังโฟลเดอร์มันไปที่เอาต์พุตเอกพจน์ .. ดังนั้น:

----> a1.ts moves into -> A <----
----> a2.ts moves into -> A <----
----> a3.ts moves into -> A <----
----> a4.ts moves into -> A <----
----> a5.ts moves into -> A <----
----> c1.ts moves into -> C <----
----> c2.ts moves into -> C <----
----> c3.ts moves into -> C <----
----> c4.ts moves into -> C <----
----> c5.ts moves into -> C <----

ฉันโชคดีที่หยุดกระบวนการ (CTRL + C) ทันทีที่ฉันสังเกตเห็นว่ามันไม่เป็นไปตามที่ตั้งใจไว้และไม่ได้ผ่านทั้งโฟลเดอร์

ดังนั้นตอนนี้ฉันมีไฟล์เหล่านั้นAแล้วและCซึ่งน้อยกว่า Gb และด้วยรูปลักษณ์ของมันคือวิดีโอเดี่ยว

มี 50Gb unaccounted สำหรับในการใช้งานดิสก์ทั้งหมดของโฟลเดอร์ แต่พื้นที่ดิสก์โดยรวมของคอมพิวเตอร์ยังคงเหมือนเดิม ทำให้ฉันคิดว่าไฟล์จะไม่ถูกลบ?

ความช่วยเหลือใด ๆ ชื่นชมขอบคุณ :)

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


3
และไม่ไดเรกทอรีชื่อA, Bและอื่น ๆ ที่มีอยู่ก่อนที่จะใช้สคริปต์หรือไม่ ถ้าไม่ใช่คุณเพิ่งเปลี่ยนชื่อไฟล์ ไฟล์ทั้งหมดที่ชื่อขึ้นต้นด้วยaหรือAถูกเปลี่ยนชื่อเป็นAดังนั้นไฟล์ที่ถูกเปลี่ยนชื่อล่าสุดเท่านั้นที่รอดชีวิตมาได้ไฟล์อื่น ๆ จะถูกเขียนทับ ในการเรียกตัวแปรdirไม่ได้สร้างไดเรกทอรี!
mook765

2
นั่นคือวิธีที่มันตีความเช่นกัน "ไฟล์ที่ถูกเปลี่ยนชื่อครั้งล่าสุดรอดชีวิตมาแล้ว" ฮา ไดเรกทอรีไม่มีอยู่ฉันควรจะเพิ่ม 'สัมผัส' สำหรับแต่ละมือก่อน ขอบคุณที่ชี้แจง
ฉันเป็นเครื่องคิดเลข TI

4
+1 สำหรับ ".. คุณธรรมของเรื่องราวให้เรียกใช้สคริปต์ของคุณในไฟล์จำลองก่อน!"
sudodus

4
เคล็ดลับที่จะหลีกเลี่ยงปัญหาเช่นนี้: ใช้mv "$file" "$dir/"กับส่วนท้าย/; แล้วถ้า$dirไม่อยู่mvจะเกิดข้อผิดพลาดแทนที่จะเปลี่ยนชื่อไป$file $dirยังพิจารณาและmv -i mv -nและมักจะทำmkdir -pก่อนที่จะย้ายสำหรับวัดดี
marcelm

3
@ Sudodus ศีลธรรมที่ดียิ่งขึ้น "สำรองข้อมูลของคุณเสมอ!"
Jon Bentley

คำตอบ:


15

ฉันคิดว่านี่เป็นปัญหา: คุณควรสร้างไดเรกทอรี A, B, C ... Z ถ้าคุณทำmvคำสั่งควรย้ายไฟล์ไปยังไดเรกทอรีเหล่านั้น

แต่ถ้าไม่ใช่mvคำสั่งจะย้ายไฟล์ไปยังไฟล์ที่มีชื่อ A, B, C ... และฉันคิดว่านี่คือสิ่งที่คุณทำ

ในการทำให้ shellscript ปลอดภัยยิ่งขึ้นคุณควรทำให้มันสร้างไดเรกทอรี (หากยังไม่ได้อยู่ที่นั่น) ก่อนที่คุณจะเริ่มการเคลื่อนย้าย

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)

for dir in "${dirs[@]}"
do
 mkdir -p $dir
done

หากคุณต้องการให้สิ่งต่าง ๆ ปลอดภัยยิ่งขึ้นคุณสามารถใช้mvกับ-iตัวเลือกนี้ได้

   -i, --interactive
          prompt before overwrite

1
การเพิ่มtouchเป็นส่วนย่อยที่ดีmkdirในการหลีกเลี่ยงความขัดแย้งในกรณีที่เรียกใช้สคริปต์หลายครั้งหรือไม่
ฉันเป็นเครื่องคิดเลข TI

2
touchสร้างไฟล์หากไม่มีชื่อ ดังนั้นจะไม่ทำสิ่งที่คุณต้องการในกรณีนี้ mkdir -pสามารถจัดการกับการใช้สคริปต์ได้หลายครั้ง
sudodus

6
อีกวิธีที่ง่ายกว่าที่คุณสามารถทำให้mvปลอดภัยยิ่งขึ้นคือการเข้าสู่นิสัยของการเพิ่มเครื่องหมายสแลชต่อท้ายชื่อเป้าหมายเมื่อเป้าหมายคือไดเรกทอรีเช่นmv "$file" "$dir/"
steeldriver

7

@Sudodus แล้วอธิบายสิ่งที่ผิดไป แต่นี่เป็นรุ่นที่เรียบง่ายของสคริปต์ของคุณสำหรับครั้งต่อไป:

for letter in {a..z}; do 
    dir=${letter^}
    mkdir -p -- "$dir" 
    mv -- "$letter"* "${letter^^}"* "$dir"/
done

คำอธิบาย

  • for letter in {a..z}; do: {a..z}ขยายเป็นอักษรตัวพิมพ์เล็กทั้งหมดระหว่างaและz:

    $ echo {a..z}
    a b c d e f g h i j k l m n o p q r s t u v w x y z

    $letterดังนั้นนี้จะย้ำกว่าตัวอักษรกรณีที่ต่ำกว่าทุกประหยัดแต่ละ

  • dir=${letter^}: ไวยากรณ์${var^^}ส่งคืนเนื้อหาของตัวแปร$varด้วยอักขระตัวแรกในตัวพิมพ์ใหญ่ (เนื่องจากมีเพียงหนึ่งตัวอักษรนั่นคือทั้งหมดที่เราต้องการ) ดังนั้นถ้า$letterเป็นaแล้ว${letter^^}เป็นAและดังนั้นจึงจะเป็นรุ่นพิมพ์ใหญ่ในปัจจุบัน$dir$letter

  • mkdir -p -- "$dir": สร้างไดเรกทอรี หากมีอยู่แล้วไม่ต้องทำอะไร ( -p) -- หมายถึงการสิ้นสุดของตัวเลือกที่-และเป็นประโยชน์ในการป้องกันชื่อเริ่มต้นด้วย
  • mv -- "$letter"* "${letter^}"* "$dir" : ย้ายทุกไฟล์ (หรือไดเรกทอรี) ไปยังเป้าหมายที่เกี่ยวข้อง

ปัญหานี้คือมันจะย้ายไดเรกทอรีใด ๆ ที่คุณอาจมี มันจะไม่ย้ายไดเรกทอรีเป้าหมายเพราะพวกมันยังไม่มีอยู่หรือคุณจะพยายามย้ายมันเข้าหาตัวเอง แต่สิ่งใด ๆ ที่มีอยู่ซึ่งไม่ใช่เป้าหมายจะถูกย้าย

หากเป็นปัญหาคุณจะต้องทำสิ่งนี้:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done

อะไรคือความแตกต่างระหว่าง${letter^}และ${letter^^}และหากเหมือนกันทำไมใช้${letter^^}แทน$dir?
คดีกองทุนของโมนิกา

1
@NicHartley ${var^}ใช้อักษรตัวแรกเป็นตัว${var^^}พิมพ์ใหญ่เท่านั้นในขณะที่ตัวพิมพ์ใหญ่ทั้งหมด มันไม่ได้สร้างความแตกต่างที่นี่เนื่องจาก$letterมีเพียงจดหมายฉบับเดียว
terdon

นี่เป็นคำตอบที่สมบูรณ์แบบยกเว้นคุณอาจต้องการเพิ่มเลเยอร์ความระมัดระวังเป็นพิเศษโดยการเพิ่มไดเรกทอรีสแลช$dirในmvคำสั่ง (ในรูปแบบปัจจุบันมันจะล้มเหลวหากไฟล์ที่มีมาก่อนมีชื่อตัวพิมพ์ใหญ่ตัวอักษร)
Stig Hemmer

@ StigHemmer อ๊ะใช่แน่นอน เป็นจุดที่ดีมากขอบคุณ แก้ไขคำตอบแล้ว
terdon

4

แทนที่จะตรวจสอบทุกไฟล์จากอาเรย์พจนานุกรมซึ่งสร้างการวนซ้ำมากคุณสามารถจับคู่ไฟล์กับรูปแบบได้

จัดเรียงพื้นฐานมาก:

#!/bin/bash

videos=./videos
sorted=./sorted

# sort types link,move.
sort_type=link

find "$videos" -maxdepth 1 -type f \
   \( -name '*.avi' -o -name '*.mkv' -o -name '*.mp4' \) -print0 |

while IFS= read -r -d ''; do

    b=$(basename "$REPLY")
    c=${b::1}

    case $c in
        [a-zA-Z]) label=${c^} ;; [0-9]) label="0-9" ;; *) label="_" ;;
    esac

    [[ ! -d "$sorted/$label" ]] && mkdir -p "$sorted/$label"

    if [[ -L $sorted/$label/$b ]] || [[ -e $sorted/$label/$b ]]; then
        echo "File/link: '$b' exists, skipping."
        continue
    fi

    case $sort_type in
        link)
            ln -rfst "$sorted/$label" -- "$REPLY"
            ;;
        move)
               mv -t "$sorted/$label" -- "$REPLY"
            ;;
    esac
done

บางทีฉันอาจทำผิด แต่ที่แน่นอนไม่ทำงานสูญเสียสิบไฟล์ฮ่า ๆ ฉันมีปัญหาในการเข้าใจส่วนตอบกลับหรือไม่ เจตนาคืออะไรสิ่งที่เหลืออยู่ (ภายในโฟลเดอร์ ABCD .... ) คือไฟล์นามแฝงที่ชี้ไปที่ .. alias alias
ฉันเป็นเครื่องคิดเลข TI

ตั้งค่าซ้ำเป็นบรรทัดอินพุตที่อ่านโดยคำสั่ง read builtin เมื่อไม่มีการระบุอาร์กิวเมนต์
bac0n

ตกลงแล้วในตอนท้ายคุณจะln -rfst "$ เรียง / $ ป้ายกำกับ" - "$ REPLY"ทำไมนามแฝงถ้าเราเพิ่งย้ายพวกเขาด้วย mv?
ฉันเป็นเครื่องคิดเลข TI

โดยค่าเริ่มต้นมันจะสร้างลิงก์จากไดเรกทอรี "วิดีโอ" ลงในไดเรกทอรีที่เรียงลำดับของคุณหากคุณต้องการ mv คุณต้องยกเลิกการใส่เครื่องหมาย "ย้ายวิดีโอ" และแสดงความคิดเห็น "เชื่อมโยงวิดีโอ" (ฉันคิดว่า
symlink

... ไม่ใช่ทั้งสองอย่างในเวลาเดียวกัน
bac0n

2

ปกป้องใน. bashrc ของคุณ:

alias mv="mv -n --backup=numbered"

1
@ Zanna ขอบคุณสำหรับสิ่งนั้น! เพิ่มคำพูด

มีคำถามเพียงเล็กน้อยที่จะต้องแน่ใจว่า (เป็นมือใหม่) การเพิ่มไฟล์. zshrcนั้นใช้ได้เช่นกัน (หากใช้ ZSH) ในman mvมันบอกว่ามัน-n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)ไม่สำคัญว่าจะมีแท็ก-nก่อนที่จะติดตามแท็ก? = --backup เลขจะสร้างคู่ของแต่ละขวาไม่ว่าบิต overkill (และพื้นที่พลังงาน / การบริโภค) เมื่อต้องรับมือกับปรากฏการณ์ขนาดใหญ่ไฟล์วิดีโอ (พูดคุยเทราไบต์) ขอบคุณมาก!
ฉันเป็นเครื่องคิดเลข TI

2

สำหรับเรคคอร์ดบางวิธีในการหยุดmvเขียนทับไฟล์ที่มีอยู่:

  • หากคุณต้องการที่จะย้ายไปยังไดเรกทอรีผนวกเฉือนเพื่อเป้าหมายคือใช้แทนmv "$file" "$dir"/ mv "$file" "$dir"หาก$dirไม่มีอยู่หรือไม่ใช่ไดเรกทอรีmvจะบ่น:

    $ touch a
    $ mv a z/
    mv: cannot move 'a' to 'z/': Not a directory
    $ touch z
    $ mv a z/
    mv: failed to access 'z/': Not a directory

    สิ่งนี้ทำให้การเรียกของระบบrename("a", "z/")ดังนั้นจึงควรปลอดภัยจากช่องโหว่เวลาในการตรวจสอบถึงเวลาในการใช้งานในกรณีที่มีคนจัดการไฟล์ชุดเดียวกันในเวลาเดียวกัน

  • mv -t "$dir" "$file"อีกวิธีหนึ่งคือการใช้งาน อีกครั้งมันจะบ่นถ้า$dirไม่ใช่ไดเรกทอรี

  • ใช้-nตัวเลือกเพื่อป้องกันการเขียนทับไฟล์ที่มีอยู่:

    -n, --no-clobber
        do not overwrite an existing file

    มันจะไม่หยุดมันจากการเปลี่ยนชื่อไฟล์แรก แต่มันจะไม่ทำลายมันกับคนอื่น ๆ

    สิ่งนี้ดูเหมือนจะเรียกว่าธรรมดาrename()ดังนั้นจึงอาจไม่ปลอดภัยเมื่อใช้งานพร้อมกัน (มีrenameat2()ที่รองรับธงเพื่อป้องกันการเขียนทับ)


1

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

  • 'ฮาร์ดลิงก์' หนึ่งไฟล์ขึ้นไปไปยังไฟล์เดียวกันมีอยู่ที่อื่นบนระบบไฟล์
  • หนึ่งกระบวนการขึ้นไปมีไฟล์เปิดอยู่

filesystems Unix อนุญาตให้มากกว่าหนึ่งรายการไดเรกทอรีเพื่ออ้างถึงที่แน่นอนเดียวกันเนื้อหาของแฟ้ม สิ่งนี้เรียกว่า ' ฮาร์ดลิงก์ ' คุณสามารถสร้างฮาร์ดลิงก์ด้วยlnคำสั่งโดยไม่ใช้-sตัวเลือก (soft / symbolic) ทั่วไป ตราบใดที่มีฮาร์ดลิงก์อย่างน้อยหนึ่งลิงก์อยู่ในเนื้อหาไฟล์ระบบจะไม่นำกลับมาใช้ใหม่

(หมายเหตุด้านสิทธิ์มักจะนำไปใช้กับเนื้อหาไฟล์ไม่ใช่รายการไดเรกทอรีนี่คือเหตุผลที่ผู้ใช้ทั่วไปบางครั้งสามารถลบไฟล์ที่เป็นเจ้าของrootแต่ไม่ได้เขียนมันการดำเนินการลบจะแก้ไขโฟลเดอร์ไม่ใช่ตัวไฟล์เอง )

ระบบไฟล์จะไม่นำเนื้อหาไฟล์มาใช้ใหม่ตราบใดที่กระบวนการอย่างน้อยหนึ่งเปิดไฟล์ แม้ว่าไม่มีรายการไดเร็กทอรีอยู่ระบบไฟล์จะไม่พิจารณาพื้นที่ว่างจนกว่าจะไม่มีกระบวนการใดเปิดอยู่ ไฟล์สามารถกู้คืนได้จากระบบไฟล์เสมือน/proc/<pid>/fdโดยrootตราบใดที่ไฟล์ยังคงเปิดอยู่ (ขอบคุณ @fluffysheap)


1
หากเกิดขึ้นกับกระบวนการที่เปิดไฟล์คุณสามารถกู้คืนได้โดยค้นหาใน / proc / <pid> / fd ดูsuperuser.com/questions/283102/…
fluffysheap
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.