web-dev-qa-db-ja.com

ディレクトリ内のすべてのファイルの名前を、ファイル名のmd5ハッシュに変更します(コンテンツではありません)

私はlinux /コマンドラインを使用するのが初めてで、MySQLデータベースのMD5暗号化された名前と一致するように、10K +ファイル(一意の名前)の名前を暗号化する必要があります。
ファイルのディレクトリの名前を変更する方法とファイルのハッシュを取得する方法(mdsum?)を見てきましたが、ハッシュのハッシュを取得する方法にこだわっていますファイル名、拡張子を保持する生成されたハッシュにそのファイルの名前を変更します。

mynicepicture.jpg > fba8255e8e9ce687522455f3e1561e53.jpg 

それは単純な名前変更またはmv行であるように見えますが、頭を悩ませることはできません。
あなたの洞察に感謝します

PS私が探しているものに近いいくつかの例でPerl関数の使用を見てきましたが、それらをどこでどのように使用するかわかりません。

9
BradH

使用するシェルを指定しなかったため、Bashと仮定しています。他のシェルを使用するには、答えを調整する必要があります。

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

スクリプトのバージョン:

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

この単純なforループは、現在のディレクトリ内のすべてのファイルを取得し、その名前のmd5合計を計算して出力します。これを使用して機能を確認します。名前の変更を開始する場合は、2番目のechomvに置き換えます。

解説

  • _echo -n "$i" | md5sum_ –ファイル拡張子( Piping )を含む完全なファイル名のmd5合計を計算し、拡張機能_echo -n "$i"_を次のいずれかに変更します。

    _${i%%.*}
    sed 's/\..*//' <<< "$i"
    echo "$i" | sed 's/\..*//'
    _
  • sum=$(…) – __を実行し、出力を_$sum_に保存します( Command Substitution

  • _${sum%% *}_ –次のいずれかと同じように、最初のスペース( Parameter Substitution )まですべてを出力します。

    _$(sed 's/ .*//' <<< "$sum")
    $(echo "$sum" | sed 's/ .*//')
    _
  • _${i##*.}_ –次のいずれかと同様に、最後のドット(パラメーター置換)の後のすべてを出力します。

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

別のフォルダーにあるファイルの名前を再帰的に変更する必要がある場合は、findを_-exec_オプションとともに使用します。

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

このbashスクリプトは、GNU coreutilsからのmd5sumユーティリティを使用して、任意のパス名のベース名(sans拡張子)からMD5ハッシュを計算します。ヘルパー関数md5nameは実際の計算を行い、完全なパスと拡張子を付けて新しい名前を出力します。

md5name関数はawkを使用して、指定されたパス名の一部とmd5sumの結果から新しい名前を組み立てます。

単独で使用されている関数の例:

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

...ここで、c9e89fa443d16da4b96ea858881320c9は、文字列file name hereのMD5ハッシュです。

上部のスクリプトからechoを削除して、実際にファイルの名前を変更します。ある時点でファイル名を元の名前に復元する必要がある場合は、元のスクリプトの出力をファイルに保存する(echoを適切な場所に)ことができます。

このtwiceを一連のファイルで実行すると、MD5ハッシュのMD5ハッシュが計算され、元のファイル名は、ファイルを呼び出すたびにどのファイルが呼び出されるかについて注意を払わない限り、回復できなくなります。脚本。

6
Kusalananda

Perlrenameを使用:

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"' {} +

(削除する -n幸せなとき)。

5

これは、私がよく使用するアプローチです。

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

「ls」コマンドは、テキスト行のストリームを生成します。 「sed」コマンドは、パターンマッチングルールで各行を変換します。 「sed」コマンドは「mv」コマンドを出力し、シェル「sh」を介してパイプされて実行されます。 「mv」コマンドのパラメーターは、ファイルの名前を変更する「mv oldfilename newfilename」のようなものです。最後のドットの前の部分を取得し、それを「md5sum」コマンドの入力にエコーし、出力からハッシュだけを取得するsedコマンドで新しいファイル名を作成します。

プロセスを歩いて、最初のファイルをリストします( 'head -n 3'で最初の3行だけを表示します):

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

次に、sedによる変換について考えます(まだシェルを介して生成されたコマンドをパイプしていません)。

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

3つの一致パターンがあります。

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

Sedを使用して入力ファイル名を「mv filename NEWfilename」に置き換えたいのですが、シェルを介してコマンドをパイプ処理しているので、次のようにmd5sumを取得するコマンドを生成できます。

echo "1000-26092016" | md5sum
    55b18a6b0add4a318b0079e18512b4e8  -

ハッシュだけを取得する

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

UNIXシェルでは、バックティック演算子( `some_command`)を使用してサブコマンドを実行できます。たとえば、

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

Mvコマンドに戻って、md5sumを取得するために、sedで「there」をbacktickコマンドに置き換えて「mv here there」を生成したいと思います。 sed replace-string内の文字列は次のように始まります

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

しかし、sedが文字列を見つける前にbackticked-commandが実行されているため、各ファイル名に同じハッシュを作成していることは明らかです。シェルがbacktickコマンドの実行を停止してsedがバックティックを出力するようにするには、スラッシュを(パイプ文字にも)追加する必要があるため、次のようにします。

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

出力では、スペースの場合にファイル名を引用符で囲む必要があるため、

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"

だから、これをシェルにパイプすることで試してみましょう:

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

うまくいきましたか?私は推測する:

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

これがクロスチェックのアプローチです。 「ls」オプション「-i」を使用して、UNIXファイルシステム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"

または、「貼り付け」コマンド( '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
2
jmullee

AWKアプローチの場合:

_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)
           }'
_

最近のfindコマンドは、入力用のディレクトリを必要としない_._が想定されているため、[ディレクトリ]は空白のままでもかまいません。 _-type f_はファイルのみを検索します。これは、_md5sum_がディレクトリを好みません。実行中にディレクトリ名を変更することはお勧めできません。一部のファイルのみを使用する場合は、_-iname pattern_を使用します。 _-iname \*.dat_、大文字と小文字が重要な場合は、_-name_ではなく_-iname_を使用します。

match(...); sub(...)の部分は、ファイル名の一部を抽出し、入力文字列でそれらを置き換えます。 _"^"_と_"$"_は、パス/拡張子を繰り返す可能性のある文字列を置き換えないようにするために[pre/ap]が付いていることに注意してください。

名前の変更を実際に実行するには、print(com)system(com)に置き換えます。

実際のファイルの_md5sum_を名前として使用する場合は、_md5sum_が合計を出力し、ファイル名を入力するという事実を使用して、次のようなことを行うことができます。

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

_while read sum file_は、_md5sum_コマンドの結果である2つの引数を取り、sumおよびfile変数をそれらに割り当てます。 sumにはスペースを入れてはならないので、readは正常に機能します。

明らかに_[echo]_は実際に実行するときに削除する必要がありますが、スクリプト化された変更をテストして実行前に検索をテストする場合は常に良い方法です。

これはすべて、bashを実行していることを前提としています。また、これは1つの長い行として入力できます。

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

私はその1行の答えが好きですが、ファイル名を解析するので壊れます。私もそれをシャハッシュで少し上げました。

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

私はそれもファイルを引き出して、コマンドが入力された場所のベースにそれらを置くと思います。

ありがとう。

0
GoofProg