Các tập tin 'Ghi đè', không gian vẫn bị chiếm dụng, chúng có bị mất không?


11

Vì vậy, tôi không kiên nhẫn sử dụng tập lệnh sau trên máy chủ 19.04 của mình để cố gắng di chuyển một loạt các tệp video vào các thư mục có tiền tố:

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

Không biết nó đã sai ở đâu, nhưng thay vì di chuyển các tệp vào các thư mục, nó đã chuyển sang một đầu ra số ít .. vì vậy:

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

Tôi may mắn đã dừng quá trình (CTRL + C) ngay khi tôi nhận thấy nó không diễn ra như dự định và không đi qua toàn bộ thư mục.

Vì vậy, bây giờ tôi đã có những tệp đó AC, ít hơn một Gb, và bởi vẻ ngoài của nó là một video SINGLE.

Có 50Gb chưa được tính trong tổng số lần sử dụng đĩa của thư mục, nhưng không gian đĩa chung của máy tính vẫn giữ nguyên. Làm tôi nghĩ rằng các tập tin không bị xóa?

Bất kỳ trợ giúp đánh giá cao, cảm ơn :)

Chỉnh sửa: các tệp thực sự đã biến mất, chỉ còn lại tệp cuối cùng được ghi lại, tất cả chỉ mất một thời gian để thông tin sử dụng đĩa cập nhật .. đạo đức của câu chuyện, chạy tập lệnh của bạn trên các tệp giả trước đó!


3
Và các thư mục có tên A, Bvà như vậy đã tồn tại trước khi chạy tập lệnh? Nếu không bạn chỉ đổi tên các tập tin. Tất cả các tệp có tên bắt đầu bằng ahoặc Ađã được đổi tên thành A, vì vậy chỉ có tệp được đổi tên cuối cùng còn tồn tại, các tệp khác được ghi đè. Để gọi một biến dirkhông tạo một thư mục!
mook765

2
đó là cách nó giải thích nó là tốt. "tập tin đổi tên cuối cùng còn tồn tại" ha. thư mục không tồn tại, tôi nên thêm một 'chạm' cho mỗi thư mục trước. cảm ơn vì đã làm rõ
Tôi là một máy tính TI

4
+1 cho ".. đạo đức của câu chuyện, chạy tập lệnh của bạn trên các tệp giả trước đó!"
sudodus

4
Một mẹo để tránh các vấn đề như thế này: Sử dụng mv "$file" "$dir/", với dấu vết /; sau đó nếu $dirkhông tồn tại, mvsẽ lỗi thay vì đổi tên $filethành $dir. Cũng xem xét mv -imv -n. Và luôn luôn làm một mkdir -ptrước khi di chuyển, cho các biện pháp tốt.
marcelm

3
@sudodus Thậm chí đạo đức tốt hơn, "Luôn sao lưu dữ liệu của bạn!".
Jon Bentley

Câu trả lời:


15

Tôi nghĩ đây là vấn đề: Bạn nên tạo các thư mục A, B, C ... Z. Nếu bạn đã làm, mvlệnh sẽ di chuyển các tệp vào các thư mục đó.

Nhưng nếu không, mvlệnh sẽ di chuyển các tệp thành các tệp có tên đó là A, B, C ... và tôi nghĩ đây là những gì bạn đã làm.

Để làm cho shellscript an toàn hơn, bạn nên làm cho nó tạo các thư mục (nếu chúng chưa có ở đó) trước khi bạn bắt đầu di chuyển.

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

Nếu bạn muốn mọi thứ trở nên an toàn hơn, bạn cũng có thể sử dụng mvvới -itùy chọn

   -i, --interactive
          prompt before overwrite

1
thêm vào sẽ touchlà một viện trợ tốt mkdirđể tránh xung đột trong trường hợp chạy tập lệnh nhiều lần?
Tôi là một máy tính TI

2
touchtạo một tập tin nếu tên không tồn tại Vì vậy, nó sẽ không làm những gì bạn muốn trong trường hợp này. mkdir -pcó thể xử lý bằng cách sử dụng tập lệnh nhiều lần.
sudodus

6
Một cách đơn giản khác mà bạn có thể làm cho mvan toàn hơn là tập thói quen thêm dấu gạch chéo vào tên mục tiêu khi mục tiêu là một thư mục tức làmv "$file" "$dir/"
Steeldo

7

@Sudodus đã giải thích những gì đã sai, nhưng đây là phiên bản đơn giản hơn của tập lệnh của bạn cho lần tiếp theo:

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

Giải trình

  • for letter in {a..z}; do: {a..z}mở rộng cho tất cả các chữ cái viết thường giữa az:

    $ 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

    Vì vậy, điều này sẽ lặp đi lặp lại trên tất cả các chữ cái viết thường, lưu từng chữ cái $letter.

  • dir=${letter^}: cú pháp ${var^^}trả về nội dung của biến $varcó ký tự đầu tiên viết hoa (vì cái này chỉ có một ký tự, đó là tất cả những gì chúng ta cần). Vì vậy, nếu $lettera, thì ${letter^^}A, và do đó $dirsẽ là phiên bản chữ hoa của dòng điện $letter.

  • mkdir -p -- "$dir": tạo thư mục. Nếu nó đã tồn tại, không làm gì cả ( -p). Dấu -- hiệu biểu thị sự kết thúc của các tùy chọn và rất hữu ích để bảo vệ chống lại các tên bắt đầu bằng -.
  • mv -- "$letter"* "${letter^}"* "$dir" : di chuyển mọi tệp (hoặc thư mục) đến mục tiêu có liên quan.

Vấn đề với điều này, là nó cũng sẽ di chuyển bất kỳ thư mục nào bạn có thể có. Nó sẽ không di chuyển các thư mục đích, bởi vì chúng chưa tồn tại hoặc bạn sẽ cố gắng di chuyển chúng vào chính chúng, nhưng bất kỳ thư mục hiện có nào không phải là thư mục tiêu sẽ bị di chuyển.

Nếu đó là một vấn đề, bạn sẽ phải làm một cái gì đó như thế này:

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

Sự khác biệt giữa ${letter^}${letter^^}và nếu chúng giống hệt nhau, tại sao lại sử dụng ${letter^^}thay $dirthế?
Vụ kiện của Quỹ Monica

1
@NicHartley chỉ ${var^}viết hoa chữ cái đầu tiên, trong khi ${var^^}viết hoa tất cả các chữ cái. Nó không làm cho bất kỳ sự khác biệt ở đây vì $letterchỉ có một chữ cái.
terdon

Đây là một câu trả lời hoàn hảo, ngoại trừ bạn có thể muốn thêm một lớp cẩn thận bằng cách thêm dấu gạch chéo $dirvào mvlệnh. (Ở dạng hiện tại, nó sẽ thất bại nếu một tập tin preexists có tên viết hoa một chữ cái)
Stig Hemmer

@StigHemmer rất tiếc, đúng vậy. Điểm rất tốt, cảm ơn. Trả lời chỉnh sửa.
terdon

4

Thay vì kiểm tra mọi tệp dựa trên một mảng từ điển tạo ra rất nhiều lần lặp, bạn có thể khớp các tệp với các mẫu.

Sắp xếp rất cơ bản:

#!/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

Có lẽ tôi đã làm sai nhưng điều đó chắc chắn không hoạt động, mất mười tập tin lol. Tôi đang gặp khó khăn trong phần TRẢ LỜI? Mục đích của tất cả những gì còn lại (bên trong các thư mục ABCD ....) là các tệp bí danh tự trỏ đến .. bí danh
Tôi là một máy tính TI

REPLY được đặt thành dòng đầu vào được đọc bởi lệnh dựng sẵn khi không có đối số nào được cung cấp.
bac0n

ok và cuối cùng bạn làm ln -rfst "$ sort / $ nhãn" - "$ REPLY" tại sao lại là bí danh nếu chúng ta chỉ di chuyển chúng với mv?
Tôi là một máy tính TI

Theo mặc định, nó tạo các liên kết từ thư mục "videos" vào thư mục được sắp xếp của bạn, nếu bạn muốn mv chúng, bạn cần bỏ ghi chú "Di chuyển video" và nhận xét "Liên kết video" (tôi nghĩ rằng các liên kết tượng trưng ít đáng sợ hơn)
bac0n

... không phải cả hai cùng một lúc.
bac0n

2

Bảo vệ trong .bashrc của bạn:

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

1
@Zanna, Cảm ơn vì điều đó! Đã thêm dấu ngoặc kép.

Vài câu hỏi chỉ để chắc chắn (là người mới), thêm vào tệp .zshrc cũng hợp lệ (nếu sử dụng ZSH)? Trong man mv, nó nói -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)như vậy có vấn đề gì với thẻ -n trước các thẻ sau không? Các = --backup đánh số sẽ tạo ra một đôi của mỗi bên, không phải là một (terabyte nói) overkill bit (và không gian năng lượng / tiêu thụ) khi giao dịch với các file video uber-lớn là. Cảm ơn !
Tôi là một máy tính TI

2

Đối với bản ghi, một số cách để ngăn chặn mvghi đè các tệp hiện có:

  • Nếu bạn muốn di chuyển đến một thư mục, hãy thêm một dấu gạch chéo vào mục tiêu, tức là sử dụng mv "$file" "$dir"/thay vì mv "$file" "$dir". Nếu $dirkhông tồn tại hoặc không phải là một thư mục, mvsẽ khiếu nại:

    $ 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

    Điều này dường như thực hiện cuộc gọi hệ thống rename("a", "z/"), vì vậy nó phải an toàn trước các lỗ hổng kiểm tra thời gian sử dụng, trong trường hợp ai đó đang xử lý cùng một bộ tệp cùng một lúc.

  • Cách khác, sử dụng mv -t "$dir" "$file". Một lần nữa, nó sẽ phàn nàn nếu $dirkhông phải là một thư mục.

  • Sử dụng -ntùy chọn để ngăn ghi đè các tệp hiện có:

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

    Nó sẽ không ngăn nó đổi tên tập tin đầu tiên, nhưng sau đó nó sẽ không làm hỏng nó với những người khác.

    Điều này dường như gọi là một đồng bằng rename(), vì vậy nó có thể không an toàn với việc xử lý đồng thời. (Có những renameat2()lá cờ hỗ trợ ghi đè.)


1

Mặc dù rõ ràng không phải là trường hợp của bạn, có thể là bạn có thể làm điều này và không bị mất các tập tin. Điều này đòi hỏi một trong hai điều phải đúng:

  • Một hoặc nhiều 'liên kết cứng' đến cùng một tệp tồn tại ở nơi khác trên hệ thống tệp
  • Một hoặc nhiều quy trình có tệp mở

Các hệ thống tệp Unix cho phép nhiều mục nhập thư mục tham chiếu đến cùng một nội dung tệp . Điều này được gọi là " liên kết cứng ". Bạn có thể tạo các liên kết cứng bằng lnlệnh, mà không có-s tùy chọn chung (mềm / tượng trưng). Miễn là có ít nhất một liên kết cứng tồn tại với nội dung tệp, nó sẽ không được hệ thống tệp sử dụng lại.

(Lưu ý Side, quyền thường áp dụng đối với các nội dung tập tin, không để các mục nhập thư mục. Đây là lý do tại sao một người dùng bình thường đôi khi có thể xóa một tập tin thuộc sở hữu của root, nhưng không ghi vào nó. Các Sửa hoạt động xóa thư mục, không phải là tập tin đó. )

Hệ thống tệp cũng sẽ không sử dụng lại nội dung tệp miễn là có ít nhất một quy trình mở tệp. Ngay cả khi không có mục nhập thư mục tồn tại, hệ thống tập tin sẽ không coi không gian là miễn phí cho đến khi không có quy trình nào được mở. Các tập tin có thể được phục hồi từ hệ thống tập tin ảo /proc/<pid>/fdbởi rootmiễn là các tập tin vẫn còn mở cửa. (Cảm ơn @fluffysheap.)


1
Nếu có một quá trình với tệp được mở, thì bạn có thể khôi phục nó bằng cách tra cứu nó trong / Proc / <pid> / fd. Xem superuser.com/questions/283102/ trên
fluffysheap
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.