đổi tên tất cả các tệp trong một thư mục thành hàm băm md5 của tên tệp của chúng (không phải nội dung)


11

Tôi rất mới với dòng lệnh linux / và cần mã hóa tên của các tệp 10K + (tên duy nhất) để chúng khớp với tên được mã hóa MD5 trong cơ sở dữ liệu myQuery.
Tôi đã thấy cách bạn có thể đổi tên thư mục tệp và cách lấy hàm băm của tệp ( mdsum? ) Nhưng tôi bị mắc kẹt về cách lấy hàm băm của tên tệp và sau đó đổi tên tệp đó thành hàm băm được tạo phần mở rộng tức là

mynicepicture.jpg > fba8255e8e9ce687522455f3e1561e53.jpg 

Có vẻ như đó là một sự đổi tên hoặc mvdòng đơn giản nhưng tôi không thể hiểu được.
Rất cám ơn sự hiểu biết của bạn

PS Tôi đã thấy việc sử dụng các hàm Perl trong một vài ví dụ gần với những gì tôi đang tìm kiếm nhưng không biết sử dụng chúng ở đâu / làm thế nào.


3
Bạn có chắc chắn muốn có một hàm băm từ tên tệp chứ không phải nội dung của tệp không?
Anthon

12
Lưu ý: Băm MD5 không phải là một thiết bị mã hóa. MD5 thậm chí không phải là một hàm băm mật mã. Băm, bất kỳ hàm băm nào, là một phép biến đổi một chiều của tập dữ liệu thành một số. Nó không thể đảo ngược. Mã hóa thực sự có thể đảo ngược, luôn luôn (được cung cấp khóa được sử dụng để mã hóa).
Kusalananda

1
fba8255e8e9ce687522455f3e1561e53là băm MD5 cho mynicepicture, điều đó có nghĩa là phần mở rộng nên được gỡ bỏ trước khi băm?
Kusalananda

@dessert tôi có nghĩa là không có được cấp nếu bạn làm md5sum <<<"file name"các file nametập tin hiện có hay không, bởi vì nó xem xét như một chuỗi trừ cho ăn nó với tên file đang tồn tại.
αғsнιη

Câu trả lời:


14

Bạn không nói bạn muốn sử dụng shell nào, vì vậy tôi chỉ giả sử Bash - câu trả lời cần điều chỉnh để hoạt động với các shell khác.

for i in *; do sum=$(echo -n "$i"|md5sum); echo -- "$i" "${sum%% *}.${i##*.}"; done

Phiên bản script:

for i in *; do
  sum=$(echo -n "$i" | md5sum)
  echo -- "$i" "${sum%% *}.${i##*.}"
done

forVòng lặp đơn giản này lấy mọi tệp trong thư mục hiện tại, tính tổng md5 của tên của nó và xuất ra nó. Sử dụng cái này để kiểm tra chức năng, nếu bạn muốn bắt đầu đổi tên thay thế cái thứ hai echobằng cách mv.

Giải thích

  • echo -n "$i" | md5sum- tính tổng md5 của tên tệp đầy đủ bao gồm cả phần mở rộng tệp ( Đường ống ), để tách thay đổi phần mở rộng echo -n "$i"thành một trong các cách sau:

    ${i%%.*}
    sed 's/\..*//' <<< "$i"
    echo "$i" | sed 's/\..*//'
  • sum=$(…)- thực hiện và lưu kết quả đầu ra trong $sum( Thay thế lệnh )

  • ${sum%% *}- xuất mọi thứ cho đến khoảng trắng đầu tiên ( Thay thế tham số ), giống như một trong các cách sau:

    $(sed 's/ .*//' <<< "$sum")
    $(echo "$sum" | sed 's/ .*//')
  • ${i##*.} - xuất mọi thứ sau dấu chấm cuối cùng (Thay thế tham số), giống như một trong những điều sau đây:

    $(sed 's/.*\.//' <<< "$i")
    $(echo "$i" | sed 's/.*\.//')

Nếu bạn cần đổi tên các tệp đệ quy trong các thư mục khác nhau, hãy sử dụng findvới -exectùy chọn.


6
#!/bin/bash

md5name () {
    local base=${1##*/}
    local ext=${base##*.}
    local dir=${1%/*}

    printf '%s' "${base%.$ext}" | md5sum |
    awk -v dir="$dir" -v ext="$ext" '{ printf("%s/%s.%s\n", dir, $1, ext) }'
}

dir=$HOME  # where your files are

for pathname in "$dir"/*; do
    test -f "$pathname" || continue
    echo mv "$pathname" "$( md5name "$pathname" )"
done

Tập bashlệnh này sử dụng md5sumtiện ích từ GNU coreutils để tính toán băm MD5 từ tên cơ sở (phần mở rộng sans) của bất kỳ tên đường dẫn cụ thể nào. Hàm trợ giúp md5namethực hiện tính toán thực tế và sẽ xuất tên mới với đường dẫn và phần mở rộng hoàn chỉnh.

Các md5namechức năng sử dụng awkđể lắp ráp tên mới từ các bộ phận của tên đường dẫn nhất định và kết quả từ md5sum.

Ví dụ về chức năng được sử dụng bởi chính nó:

$ md5name '/some/path/file name here.extension'
/some/path/c9e89fa443d16da4b96ea858881320c9.extension

... c9e89fa443d16da4b96ea858881320c9Băm MD5 ở đâu trong chuỗi file name here.

Xóa echotập lệnh ở trên cùng để thực sự đổi tên các tập tin. Bạn có thể muốn lưu đầu ra của tập lệnh gốc vào tệp (với echovị trí tại chỗ) nếu bạn tại một thời điểm nào đó cần khôi phục tên tệp về bản gốc của chúng.

Lưu ý rằng việc chạy hai lần này trên một tập hợp các tệp sẽ tính toán hàm băm MD5 của băm MD5 và tên tệp gốc sau đó sẽ không thể khôi phục được trừ khi bạn ghi chú cẩn thận về các tệp được gọi là gì sau mỗi lần chạy tập lệnh.


Giống như một FYI, awkphần có thể được thay thế bằng while read sum dummy ; do printf "%s/%s.%s\n' $dir $sum $ext ; done ;Bạn cần phải dummynắm bắt '-'.
Robert Benson

@RobertBenson Vấn đề với điều đó là tên tệp chứa khoảng trắng sẽ bị rối tung.
Kusalananda

Cuộc gọi tốt Tên tập tin với không gian là xấu xa. Tôi thích awkbản thân mình và tôi phải mất một thời gian để sử dụng bashcác tiện ích thay vì system()trongawk
Robert Benson

5

Với perl's rename:

find . -name '*.jpg' -type f -exec rename -n '
  BEGIN{use Digest::MD5 qw(md5_hex)}
  my ($dir, $name, $ext) = m{(.*)/(.*)\.(.*)}s;
  $_ = "$dir/" . md5_hex($name) . ".$ext"' {} +

(loại bỏ -nkhi hạnh phúc).


Kinh ngạc! Điều này sẽ tính tổng md5 của tên tệp mà không có phần mở rộng, bây giờ làm thế nào về tên tệp đầy đủ? OP đã không nói liệu anh ta cần nó có hay không có nó.
tráng miệng

1
Anh không nói ra, nhưng ví dụ anh đưa ra chính xác là như vậy.
Robert Benson

2

Đối với một AWKcách tiếp cận:

find [Directory] -type f [various other find options] | 
     awk '{orig=$0; 
           match($0,/^.*\//,path); sub("^"path[0], "");
           match($0, /.[[^.]+$/,ext); sub(ext[0]"$", "");
           ("echo \"" $0 "\"|md5sum") | getline;
           com=sprintf("mv \"%s\" \"%s%s%s\"", orig, p[0], $1, ext[0]);
           print(com)
           }'

Các findlệnh hiện đại không yêu cầu một thư mục cho đầu vào .được giả sử, vì vậy [Thư mục] có thể để trống. Các -type ftập tin duy nhất tìm thấy, rất tiện lợi vì md5sumkhông thích các thư mục và thay đổi tên thư mục trong khi chạy sẽ không phải là một ý tưởng hay. Sử dụng -iname patternnếu bạn chỉ muốn sử dụng một số tệp, ví dụ: -iname \*.datnếu trường hợp quan trọng, hãy sử dụng -namethay vì -iname.

Các match(...); sub(...)mảnh đang trích xuất các phần của tên tệp và thay thế chúng trong chuỗi đầu vào. Lưu ý rằng "^""$"[trước / ap] được chờ đợi để ngăn thay thế một chuỗi có thể lặp lại đường dẫn / tiện ích mở rộng.

Thay thế print(com)bằng system(com)để thực sự đổi tên.

Nếu bạn muốn sử dụng md5sumtệp thực tế làm tên, bạn có thể sử dụng thực tế là md5sumxuất tên tệp tổng và đầu vào để thực hiện một số thứ như:

 find -type f -exec md5sum '{}' ';' | 
     while read sum file ; do 
       [echo] mv "$file" "`dirname $file`/$sum".extension ; 
     done

Các while read sum filesẽ mất 2 đối số, kết quả của các md5sumlệnh, và assign sumfilebiến với họ. Vì sumkhông nên có không gian trong đó, readnên hoạt động tốt.

Rõ ràng là [echo]nên loại bỏ khi thực sự chạy, nhưng luôn luôn là một ý tưởng tốt khi kiểm tra bất kỳ thay đổi nào theo kịch bản để kiểm tra tìm kiếm trước khi chạy.

Tất cả điều này giả định rằng bạn đang chạy bash. Ngoài ra, điều này có thể được gõ là một dòng dài:

find -iname \*.jpg -exec md5sum '{}' ';' | while read sum file ; do mv "$file" "`dirname $file`/$sum".jpg ; done

1
Có vẻ như điều này sẽ băm nội dung của các tập tin. OP muốn băm tên (không có phần mở rộng).
Kusalananda

Tôi đoán nó sẽ giúp nếu tôi đọc hết câu hỏi.
Robert Benson

2

Điều này trong cách tiếp cận tôi thường thích sử dụng.

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \\`echo \1 \| md5sum \| cut -d' ' -f 1\\`.\2|" | sh -

Lệnh "ls" tạo ra một dòng các dòng văn bản. Lệnh "sed" tranforms mỗi dòng với các quy tắc khớp mẫu. Lệnh "sed" xuất ra lệnh "mv", sau đó được dẫn qua lớp vỏ "sh" để thực thi. Các tham số của lệnh "mv" giống như "mv oldfilename newfilename", đổi tên tệp. Tôi xây dựng tên tệp mới bằng lệnh sed lấy phần trước dấu chấm cuối cùng và lặp lại nó vào đầu vào của lệnh "md5sum", và sau đó chỉ lấy hàm băm từ đầu ra của nó.

Xem qua quy trình của tôi, liệt kê các tệp đầu tiên ('head -n 3' để chỉ xem 3 dòng đầu tiên):

ls | head -n 3
    1000-26092016.xml
    1000-27092016.xml
    12312-28092016.xml

Sau đó suy nghĩ về việc chuyển đổi với sed (chưa thực hiện bất kỳ lệnh nào được tạo thông qua shell)

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \1.\2|" | head -n 3
    mv 1000-26092016.xml 1000-26092016.xml
    mv 1000-27092016.xml 1000-27092016.xml
    mv 12312-28092016.xml 12312-28092016.xml

Có ba mẫu khớp:

^\(.*\)      = match from start-of-line up to a dot
\.           = matches a single dot
\([^\.]*\)$  = match 0-or-more non-dot chars from end of line

Tôi muốn sử dụng sed để thay thế một tên tệp đầu vào bằng "mv filename NEWfilename", nhưng khi tôi chuyển các lệnh qua shell, tôi có thể tạo các lệnh nhận md5sum, như thế này

echo "1000-26092016" | md5sum
    55b18a6b0add4a318b0079e18512b4e8  -

để chỉ lấy băm

echo "1000-26092016" | md5sum | cut -d' ' -f 1
    55b18a6b0add4a318b0079e18512b4e8

Trong một vỏ unix, chúng ta có thể sử dụng các toán tử backtick (`some_command`) để chạy một lệnh phụ, ví dụ như vậy

echo "howdy date there"
    howdy date there
echo "howdy `date` there"
    howdy Fri Sep 15 18:39:00 IST 2017 there

Quay lại lệnh mv, tôi muốn sed tạo ra "mv here there" bằng "there" được thay thế bằng lệnh backtick để lấy md5sum. Chuỗi bên trong chuỗi thay thế sed bắt đầu như thế này

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 `echo \1 | md5sum | cut -d' ' -f 1`.\2|" | head -n 3
    mv 1000-26092016.xml     b026324c6904b2a9cb4b88d6d61c81d1.xml
    mv 1000-27092016.xml     b026324c6904b2a9cb4b88d6d61c81d1.xml
    mv 12312-28092016.xml    b026324c6904b2a9cb4b88d6d61c81d1.xml

Nhưng rõ ràng là thực hiện cùng một hàm băm cho mỗi tên tệp, vì lệnh backticky đang được chạy trước khi sed nhìn thấy chuỗi. Để dừng shell chạy lệnh backtick để sed sẽ xuất ra backticks, chúng ta phải thêm dấu gạch chéo (cũng vào ký tự ống), vì vậy một lần nữa:

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2|" | head -n 3
    mv 1000-26092016.xml     `echo 1000-26092016 | md5sum | cut -d' ' -f 1`.xml
    mv 1000-27092016.xml     `echo 1000-27092016 | md5sum | cut -d' ' -f 1`.xml
    mv 12312-28092016.xml    `echo 12312-28092016 | md5sum | cut -d' ' -f 1`.xml

Đầu ra cũng cần tên tệp được trích dẫn trong trường hợp khoảng trắng, vì vậy

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | grep trick
    mv "a trick€€ fíle nÁme.xml" "`echo a trick€€ fíle nÁme | md5sum | cut -d' ' -f 1`.xml"

Vì vậy, hãy thử cái này bằng cách đưa nó qua một cái vỏ:

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | grep trick | sh -

Nó có hoạt động không? tôi đoán:

echo "a trick€€ fíle nÁme" | md5sum
    629db9c3071928ba0746f18444713b65  -
ls 629db9c3071928ba0746f18444713b65*
    629db9c3071928ba0746f18444713b65.xml

Đây là một cách tiếp cận để kiểm tra chéo; sử dụng tùy chọn "ls" "-i" để xuất nút i của hệ thống tập tin unix (không thay đổi với "mv"):

ls -1i | sort -n > .before
ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | sh -
ls -1i | sort -n > .after
cut -d' ' -f 1 .before | while read I ; do echo "mv'd \"`grep ${I} .before`\" to \"`grep ${I} .after`\"" | sed "s| *$I *||g" ; done | head -n 3
    mv'd "1000-26092016.xml" to "55b18a6b0add4a318b0079e18512b4e8.xml"
    mv'd "1000-27092016.xml" to "b1baa80d99d5edf85c8aeb98185dd440.xml"
    mv'd "12312-28092016.xml" to "2b2d692bd047b64c99f7b9161349d430.xml"

Hoặc, sử dụng lệnh "dán" (gói 'coreutils')

paste .before .after | head -n 3
    36703389 1000-26092016.xml  36703389 55b18a6b0add4a318b0079e18512b4e8.xml
    36703390 1000-27092016.xml  36703390 b1baa80d99d5edf85c8aeb98185dd440.xml
    36703391 12312-28092016.xml 36703391 2b2d692bd047b64c99f7b9161349d430.xml

0

Tôi thích câu trả lời một dòng đó, nhưng nó bị hỏng vì nó phân tích tên tệp. Tôi cũng đã nâng nó lên một chút với băm sha.

find -iname "*.jpg" -exec sha1sum '{}' ';' | while read sum file ; do mv -v "$file" "`dirname '$file'`/$sum".jpg ; done

Tôi nghĩ rằng nó cũng kéo các tập tin ra và đặt chúng ở cơ sở nơi lệnh được nhập.

Cảm ơn.


1
Chúng tôi có lẽ nên tham khảo lại câu trả lời mà bạn dựa trên.
Jeff Schaller
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.