Đồng nát

Khôi phục và chuyển ảnh vào thư mục tương ứng với ngày chụp

Một người anh có cái ổ 4TB cắm vào máy mà đợi mãi không thấy đâu. Chạy Disk Utility kiểm tra thì báo là “file system check exit code is 8”. Cư dân mạng khuyên là đừng sửa gì thêm mà chỉ nên dùng phần mềm khôi phục dữ liệu rồi chép sang ổ cứng mới.

Thật may là anh có Disk Drill nên cho chạy suốt 24 giờ thì cũng hiện ra được hơn chục vạn cái ảnh NEF (ảnh raw của Nikon). Chép sang ổ cứng mới mất khoảng chục tiếng.

Thật không may là ảnh thì cứu được nhưng cấu trúc thư mục thì không. Và vì không thay đổi tuỳ chọn mặc định nên hơn 100.000 (một trăm nghìn) tập tin sau khi chép xong đều nằm ở thư mục gốc của ổ cứng. Chứ không phải là mỗi 1000 tập tin sẽ được phân vào một thư mục như khi Disk Drill tái cấu trúc dữ liệu.

Kết quả quét ổ cứng

Nhiều thế thì mỗi lần truy cập vào ổ cứng thì sẽ mất nửa tiếng mới hiện ra danh sách tập tin. Nói cách khác là cứ để thế thì cũng không dùng được.

Anh xem qua thì thấy là “Date Modified” (mtime) trùng với thời gian chụp lưu trong exif. Có vẻ như Disk Drill đã thay đổi mtime theo thông tin trong exif. Ngay cả tên tập tin cũng được đổi theo tên máy.

Có thể lấy mtime khi chạy ls -l. Thế tức là có thể di chuyển ảnh vào thư mục tương ứng với ngày chụp (chính xác là mtime).

Một trăm nghìn tập tin là một danh sách quá dài. Anh chạy ls mà đợi dài cổ chả thấy đâu. Thật may là các tập tin được Disk Drill đánh số thứ tự dạng XYZ_000001.nef nên có thể dễ dàng chia nhỏ số lượng tập tin để có thể xử lí được.

Như vậy các bước thực hiện sẽ như sau:

  1. Chia từng 10.000 ảnh vào từng thư mục riêng (thư mục làm việc)
  2. Tạo các thư mục theo ngày tháng dựa trên mtime của ảnh:
    ls -lt: tạo danh sách tập tin theo mtime
    | sed: xác định tên thư mục dạng YYYY-Mon-D
    | uniq: hoàn thiện danh sách thư mục
    | xargs mkdir {}: tạo thư mục theo ngày tháng
  3. Di chuyển tập tin vào thư mục theo ngày tháng:
    ls -lt: tạo danh sách tập tin theo mtime
    | sed: tạo các câu lệnh mv 'file\ name.nef' YYYY-Mon-D
    | xargs -I {} sh -c "{}": thực hiện lệnh mv
  4. Đổi tên thư mục ngày tháng từ dạng YYYY-Mon-D sang dạng YYYY-MM-DD

Và đây là tập lệnh hoàn chỉnh:

# Mẫu tìm kiếm ngày tháng trong ls -l
p='.* [0-9]+ ([a-zA-Z]+) [[:space:]]?([0-9]+)[[:space:]]+([0-9]+) (.*)'

dir=`pwd`

# 1. Tạo các thư mục làm việc
# Chuyển một vạn tập tin vào mỗi thư mục này
for i in `seq -f '%02g' 0 11`; do

    # Tạo thư mục làm việc
    mkdir $i

    # Di chuyển một vạn tập tin
    mv *_$i*.nef $i

    # Đến từng thư mục làm việc
    cd $dir/$i

    echo "Đang ở $i"

    # 2. Lọc và tạo thư mục ngày tháng dạng YYYY-Mon-D
    ls -lt *.nef | sed -E "s/$p/\3-\1-\2/g" | grep -v ".nef" | uniq | xargs -I {} mkdir {}

    # 3. Di chuyển tập tin vào thư mục ngày tháng
    ls -lt *.nef | \
    egrep "$p" | \
    sed -E "s@$p@\"mv \'\4\' ./\3-\1-\2/\"@g" | \
    xargs -I {} sh -c "{}"

    # 4. Đổi tên thư mục ngày tháng thành dạng: YYYY-MM-DD
    # date trong macOS không có tuỳ chọn -d nên phải dùng -jf "%Y-%b-%d"
    for d in `ls -1`; do
        mv $d $(date -jf "%Y-%b-%d" "$d" +"%Y-%m-%d")
    done

    # Chuyển về thư mục gốc
    cd $dir

done

Sau cùng anh tạo các thư mục theo năm và mở Finder rồi cần mẫn kéo thả các thư mục yyyy-mm-dd vào các năm tương ứng. Lúc này sẽ có một số thư mục trùng tên nên anh lại phải tiếp tục gộp thủ công. Loay hoay đâu đó chừng tiếng đồng hồ thì cũng xong.

Ảnh đã được phân vào thư mục theo thời gian chụp

Để tránh điều này thì các thư mục ngày tháng nên được ở một thư mục chung, tạm đặt tên là YMD. Khi đó ở bước 2, thay vì mkdir {} thì sẽ là mkdir -p ../YMD/{}

Tất nhiên là bước 3 cũng phải chỉnh lại đường dẫn.

Nhưng ngay cả khi bạn đã chọn lưu mỗi 1000 ảnh vào một thư mục thì mọi thứ vẫn rất hỗn loạn. Vì rất có thể ảnh chụp trong cùng một ngày lại nằm ở nhiều thư mục khác nhau.

Dưới đây là một đoạn mã hoàn chỉnh để bạn giải quyết toàn bộ vấn đề.

#!/bin/bash

# Chuyển ảnh khôi phục bằng Disk Drill 
# vào thư mục tương ứng với ngày chụp
#
# Áp dụng với tuỳ chọn giữ cấu trúc thư mục Group của Disk Drill

filetype='nef'

# Không thay đổi từ đây

p=".* [0-9]+ ([0-9]{4}-[0-9]{2}-[0-9]{2}) (.*$filetype)$"

dir=`pwd`

for i in $(ls -1 | grep -v 'YMD' | sed -E 's/Group //g'); do

    cd "$dir/Group $i"

    echo "----------"
    echo "$(pwd) - " $(date "+%H:%M:%S")

    #rm -f file*.jpg

    count=$(ls -1 *.$filetype 2> /dev/null | egrep -c ".${filetype}$")

    if [ "0" != "$count" ]; then

        echo "-- Đang tạo thư mục ngày tháng..."

        stat -l -t '%Y-%m-%d' *.$filetype |\
            sed -E "s/$p/\1/g" |\
            sort | uniq |\
            xargs -I {} mkdir -p "$dir/YMD/{}"

        echo "-- $count ảnh. Đang di chuyển..."

        stat -l -t '%Y-%m-%d' *.$filetype |\
            sed -E "s@$p@\"mv \'\2\' $dir/YMD/\1/\"@g" |\
            xargs -I {} sh -c "{}"

    else

        echo "-- Không có ảnh trong thư mục này!"

    fi

done

Như vậy là nếu chẳng may lần sau có bị sự cố tương tự thì sẽ mất khoảng 2 ngày để khôi phục dữ liệu (nếu may mắn) và dùng cái đoạn mã này để tiết kiệm được nửa ngày loay hoay sắp xếp thư mục.