سؤال

مواصفات المشكلة:

بالنظر إلى دليل، أريد التكرار من خلال الدليل ودليلها الفرعي غير المخفي،
وإضافة تجزئة دوامة إلى أسماء الملف غير المخفي.
إذا تم إعادة تشغيل البرنامج النصي، فسيحل محل التجزئة القديمة مع واحدة جديدة.

<filename>.<extension>   ==>  <filename>.<a-whirlpool-hash>.<extension>

<filename>.<old-hash>.<extension>   ==>  <filename>.<new-hash>.<extension>


سؤال:

أ) كيف ستفعل هذا؟

ب) من جميع الأساليب المتاحة لك، ما الذي يجعل طريقتك أكثر ملاءمة؟


الحكم:

شكرا كل ذلك، لقد اخترت إجابة Seigex سرعتها وقابلتانها.
إنه أسرع عمليا من متغيرات الباش الأخرى،
وقد عملت دون تغيير على جهاز Mac OS X.

هل كانت مفيدة؟

المحلول

تم التحديث لإصلاح:
1. أسماء الملفات مع [" أو "] باسمهم (حقا، أي حرف الآن. انظر التعليق)
2. التعامل مع MD5SUM عند تجزئة ملف مع خط عاطفي أو نيولين في اسمه
3. وظيفة الفحص الغدون
4. إعادة تصنيف المنطق فحص التجزئة لإزالة السلبيات المزدوجة

#!/bin/bash
if (($# != 1)) || ! [[ -d "$1" ]]; then
    echo "Usage: $0 /path/to/directory"
    exit 1
fi

is_hash() {
 md5=${1##*.} # strip prefix
 [[ "$md5" == *[^[:xdigit:]]* || ${#md5} -lt 32 ]] && echo "$1" || echo "${1%.*}"
}

while IFS= read -r -d $'\0' file; do
    read hash junk < <(md5sum "$file")
    basename="${file##*/}"
    dirname="${file%/*}"
    pre_ext="${basename%.*}"
    ext="${basename:${#pre_ext}}"

    # File already hashed?
    pre_ext=$(is_hash "$pre_ext")
    ext=$(is_hash "$ext")

    mv "$file" "${dirname}/${pre_ext}.${hash}${ext}" 2> /dev/null

done < <(find "$1" -path "*/.*" -prune -o \( -type f -print0 \))

يحتوي هذا الرمز على الفوائد التالية على الإدخالات الأخرى حتى الآن

  • إنه متوافق تماما مع إصدارات باش 2.0.2 وما بعده
  • لا توجد مكالمات زائدة عن ثنائيات أخرى مثل SED أو GREP؛ يستخدم توسيع المعلمة المدمج بدلا من ذلك
  • يستخدم استبدال العملية ل "البحث" بدلا من أنبوب، لا توجد شل فرعي بهذه الطريقة
  • يأخذ الدليل للعمل كوسيطة ويفحص العقلية
  • يستخدم $ () بدلا من "تدوين" لاستبدال الأوامر، يتم إهمال الأخير
  • يعمل مع الملفات مع المساحات
  • يعمل مع الملفات مع خطوط نيو
  • يعمل مع الملفات مع ملحقات متعددة
  • يعمل مع الملفات بدون امتداد
  • لا يعبر الدلائل المخفية
  • هل ليس تخطي الملفات المعقلة مسبقا، وسوف إعادة حساب التجزئة حسب المواصة

اختبار شجرة

$ Tree -a a | - .hidden_dir | `- فو | - ب | `- CD | | - F | | - g.5236b1ab46088005ed3554940390c8a7.ext | | - H.D41D8CD98F00B204E9800998ECF8427E | | - i.ext1.5236b1ab46088005ed3554940390c8a7.ext2 | `- j.ext1.ext2 | - C.ext ^ mnewline | | - F | `- g.with [أو] .ext` - f ^ jnewline.ext 4 الدلائل، 9 ملفات

نتيجة

$ Tree -a a | - .hidden_dir | `- فو | - ب | `- CD | | - f.d41d8cd98f00b204e9800998ecf8427e | | - g.d41d8cd98f00b204e9800998ecf8427e.ext | | - H.D41D8CD98F00B204E9800998ECF8427E | | - i.ext1.d41d8cd98f00b204e9800998ecf8427e.ext2 | `- J.ext1.d41d8cd98f00b204e9800998ecf8427e.ext2 | - c.ext ^ mnewline | | - f.d41d8cd98f00b204e9800998ecf8427e | `- g.with [أو] .d41d8cd98f00b204e9800998ecf8427e.ext` - f ^ jnewline.d3b07384d113edec49eaa6238ad5ff00.ext 4 الدلائل، 9 ملفات

نصائح أخرى

#!/bin/bash
find -type f -print0 | while read -d $'\0' file
do
    md5sum=`md5sum "${file}" | sed -r 's/ .*//'`
    filename=`echo "${file}" | sed -r 's/\.[^./]*$//'`
    extension="${file:${#filename}}"
    filename=`echo "${filename}" | sed -r 's/\.md5sum-[^.]+//'`
    if [[ "${file}" != "${filename}.md5sum-${md5sum}${extension}" ]]; then
        echo "Handling file: ${file}"
        mv "${file}" "${filename}.md5sum-${md5sum}${extension}"
    fi
done
  • تم اختباره على الملفات التي تحتوي على مساحات مثل "AB"
  • تم اختباره على الملفات التي تحتوي على ملحقات متعددة مثل "ABC"
  • اختبار الدلائل التي تحتوي على مساحات و / أو نقاط.
  • تم اختباره على الملفات التي تحتوي على تمديد داخل الدلائل التي تحتوي على نقاط، مثل "AB / C"
  • محدث: الآن تحديثات التحديثات إذا تغير الملف.

النقاط الرئيسية:

  • استخدام print0 الأنابيب إلى while read -d $'\0', ، لمعالجة المساحات بشكل صحيح في أسماء الملفات.
  • يمكن استبدال MD5SUM مع وظيفة التجزئة المفضلة لديك. يزيل SED المساحة الأولى وكل شيء بعد ذلك من إخراج MD5SUM.
  • يتم استخراج اسم الملف الأساسي باستخدام تعبير منتظم يجد الفترة الأخيرة التي لا تتبعها مائلة أخرى (بحيث لا تحسب هذه الفترات في أسماء الدليل كجزء من التمديد).
  • تم العثور على التمديد باستخدام Substring مع مؤشر البدء كطول اسم الملف الأساسي.

منطق المتطلبات معقدة بما يكفي لتبرير استخدام الثعبان بدلا من باش. يجب أن يوفر حلا أكثر قابلية للقراءة وقابلة للتوسيع والحفاظ عليه.

#!/usr/bin/env python
import hashlib, os

def ishash(h, size):
    """Whether `h` looks like hash's hex digest."""
    if len(h) == size: 
        try:
            int(h, 16) # whether h is a hex number
            return True
        except ValueError:
            return False

for root, dirs, files in os.walk("."):
    dirs[:] = [d for d in dirs if not d.startswith(".")] # skip hidden dirs
    for path in (os.path.join(root, f) for f in files if not f.startswith(".")):
        suffix = hash_ = "." + hashlib.md5(open(path).read()).hexdigest()
        hashsize = len(hash_) - 1
        # extract old hash from the name; add/replace the hash if needed
        barepath, ext = os.path.splitext(path) # ext may be empty
        if not ishash(ext[1:], hashsize):
            suffix += ext # add original extension
            barepath, oldhash = os.path.splitext(barepath) 
            if not ishash(oldhash[1:], hashsize):
               suffix = oldhash + suffix # preserve 2nd (not a hash) extension
        else: # ext looks like a hash
            oldhash = ext
        if hash_ != oldhash: # replace old hash by new one
           os.rename(path, barepath+suffix)

إليك شجرة اختبار دليل. أنه يحتوي على:

  • الملفات دون تمديد داخل الدلائل مع نقطة في اسمها
  • اسم الملف الذي لديه بالفعل تجزئة في ذلك (اختبار حول Idempotency)
  • اسم الملف مع اثنين من الامتدادات
  • خطوط نيو في الأسماء
شجرة $ A | - B | `- CD | | - F | | - f.ext1.ext2 | `- g.d41d8cd98f00b204e9800998ecf8427e | - c.ext ^ mnewline | `- f` - f ^ jnewline.ext1 7 الدلائل، 5 ملفات

نتيجة

شجرة $ A | - B | `- CD | | - F.0BEE89B07A248E27C83FC3D5951213C1 | | - f.ext1.614dd0e977becb4c6f7fa99e64549b12.ext2 | `- g.d41d8cd98f00b204e9800998ecf8427e | - c.ext ^ mnewline | `- f.0bee89b07a248e27c83fc3d5951213c1` - f ^ jnewline.b6fe8bb902ca1b80aa632b776d77f83.ext1 7 الدلائل، 5 ملفات

يعمل الحل بشكل صحيح لجميع الحالات.


Whirlpool Hash ليس في Stdlib في بيثون، ولكن هناك كل من Python النقي و C التي تدعمها على سبيل المثال، python-mhash.

لتثبيته:

$ sudo apt-get install python-mhash

لتستخدمها:

import mhash

print mhash.MHASH(mhash.MHASH_WHIRLPOOL, "text to hash here").hexdigest()

الإخراج: CBDCA4520CC5C131FC3A86109DD23FEE2D7FF7BE56636D398180178378944A4F41480B938608AE98DA7ECCBF39A4C79B83A8590C4CB1BACE5BC638FC92BC3E653


استدعاء whirlpooldeep في بيثون

from subprocess import PIPE, STDOUT, Popen

def getoutput(cmd):
    return Popen(cmd, stdout=PIPE, stderr=STDOUT).communicate()[0]

hash_ = getoutput(["whirlpooldeep", "-q", path]).rstrip()

git يمكن أن توفر مع الرافعة المالية للمشاكل التي تحتاج إلى تتبع مجموعة الملفات بناء على التجزئة الخاصة بهم.

لم أكن سعيدا حقا من إجابتي الأولى، لأنني قلت هناك، هذه المشكلة تبدو أفضل حلها مع بيرل. قلت بالفعل في تحرير واحد من سؤالك الذي لديك بيرل على جهاز OS X الذي ترغب في تشغيله، لذلك أعطيتها بالرصاص.

من الصعب الحصول عليها بشكل صحيح في باش، أي تجنب أي مشاكل اقتباس بأسماء الملفات الفردية، وتتصرف بشكل جيد مع أسماء ملفات الزاوية.

حتى هنا في بيرل، حل كامل لمشكلتك. يعمل على جميع الملفات / الدلائل المدرجة في سطر الأوامر.


#!/usr/bin/perl -w
# whirlpool-rename.pl
# 2009 Peter Cordes <peter@cordes.ca>.  Share and Enjoy!

use Fcntl;      # for O_BINARY
use File::Find;
use Digest::Whirlpool;

# find callback, called once per directory entry
# $_ is the base name of the file, and we are chdired to that directory.
sub whirlpool_rename {
    print "find: $_\n";
#    my @components = split /\.(?:[[:xdigit:]]{128})?/; # remove .hash while we're at it
    my @components = split /\.(?!\.|$)/, $_, -1; # -1 to not leave out trailing dots

    if (!$components[0] && $_ ne ".") { # hidden file/directory
        $File::Find::prune = 1;
        return;
    }

    # don't follow symlinks or process non-regular-files
    return if (-l $_ || ! -f _);

    my $digest;
    eval {
        sysopen(my $fh, $_, O_RDONLY | O_BINARY) or die "$!";
        $digest = Digest->new( 'Whirlpool' )->addfile($fh);
    };
    if ($@) {  # exception-catching structure from whirlpoolsum, distributed with Digest::Whirlpool.
        warn "whirlpool: couldn't hash $_: $!\n";
        return;
    }

    # strip old hashes from the name.  not done during split only in the interests of readability
    @components = grep { !/^[[:xdigit:]]{128}$/ }  @components;
    if ($#components == 0) {
        push @components, $digest->hexdigest;
    } else {
        my $ext = pop @components;
        push @components, $digest->hexdigest, $ext;
    }

    my $newname = join('.', @components);
    return if $_ eq $newname;
    print "rename  $_ ->  $newname\n";
    if (-e $newname) {
        warn "whirlpool: clobbering $newname\n";
        # maybe unlink $_ and return if $_ is older than $newname?
        # But you'd better check that $newname has the right contents then...
    }
    # This could be link instead of rename, but then you'd have to handle directories, and you can't make hardlinks across filesystems
    rename $_, $newname or warn "whirlpool: couldn't rename $_ -> $newname:  $!\n";
}


#main
$ARGV[0] = "." if !@ARGV;  # default to current directory
find({wanted => \&whirlpool_rename, no_chdir => 0}, @ARGV );

المزايا: - تستخدم في الواقع دوامة، حتى تتمكن من استخدام هذا البرنامج الدقيق مباشرة. (بعد تثبيت Libperl-Digest-Whirlpool). من السهل التغيير إلى أي دالة Digest التي تريدها، لأنه بدلا من البرامج المختلفة مع تنسيقات إخراج مختلفة، لديك واجهة Perl Digest الشائعة.

  • ينفذ جميع المتطلبات الأخرى: تجاهل الملفات المخفية (والملفات تحت الأدلة المخفية).

  • قادرة على التعامل مع أي اسم ملف محتمل دون خطأ أو مشكلة أمان. (حصل عدة أشخاص على هذا الحق في البرامج النصية شل).

  • يتبع أفضل الممارسات لاجتياز شجرة دليل، من خلال chdiring في كل دليل (مثل إجابتي السابقة، مع البحث عن -EXECDIR). هذا يتجنب مشاكل في Path_Max، ومع الإشارة إلى الدلائل أثناء تشغيلك.

  • تعامل ذكي من أسماء الملفات التي تنتهي مع. foo..txt ... -> foo..hash.txt ...

  • يعالج أسماء الملفات القديمة التي تحتوي على التجزئة بالفعل دون إعادة تسميةها ثم إعادة تسليمها مرة أخرى. (يقرأ أي تسلسل 128 أرقام سداسي عشرية محاط بها "." الشخصيات ". في حالة تصحيح كل شيء، لا يحدث أي نشاط كتابة القرص، فقط يقرأ كل ملف. يعمل الحل الحالي الخاص بك MV مرتين في الحالة المسماة بالفعل، مما يسبب بيانات تعريف الدليل يكتب. ويجري أبطأ، لأن هذا هو العمليات التي يجب تنفيذها.

  • فعال. لا توجد برامج شوكة / تم تنفيذها، في حين أن معظم الحلول التي من شأنها أن تنتهي في الواقع المنتهية في وجود شيء لكل ملف. DIGEST :: يتم تطبيق Whirlpool مع Lib المشترك الذي تم تجميعه أصلا، لذلك ليس بطيئا نقيا. يجب أن يكون هذا أسرع من تشغيل برنامج على كل ملف، ESP. للحصول على ملفات صغيرة.

  • يدعم بيرل سلاسل UTF-8، لذلك لا ينبغي أن تكون أسماء الملفات مع أحرف غير ASCII مشكلة. (لست متأكدا مما إذا كان أي تسلسلات متعددة البايات في UTF-8 يمكن أن تشمل البايت الذي يعني ASCII '.' من تلقاء نفسها. إذا كان ذلك ممكنا، فأنت بحاجة إلى معالجة السلسلة UTF-8. SED لا يعرف UTF-8 . تعبيرات بلدة باش قد.)

  • بسهولة قابلة للتوسيع. عندما تذهب لوضع هذا في برنامج حقيقي، وتريد التعامل مع المزيد من الحالات الزاوية، يمكنك القيام بذلك بسهولة شديدة. على سبيل المثال، حدد ما يجب القيام به عندما ترغب في إعادة تسمية ملف، ولكن اسم الملف المسمى في التجزئة موجود بالفعل.

  • خطأ في الأخطاء جيدة. معظم البرامج النصية لشل لديها هذا، رغم ذلك، من خلال المرور على طول الأخطاء من بروغ.

find . -type f -print | while read file
do
    hash=`$hashcommand "$file"`
    filename=${file%.*}
    extension=${file##*.}
    mv $file "$filename.$hash.$extension"
done

قد ترغب في تخزين النتائج في ملف واحد، كما في

find . -type f -exec md5sum {} \; > MD5SUMS

إذا كنت تريد حقا ملف واحد لكل هات:

find . -type f | while read f; do g=`md5sum $f` > $f.md5; done

او حتى

find . -type f | while read f; do g=`md5sum $f | awk '{print $1}'`; echo "$g $f"> $f-$g.md5; done

ها هي بلدي تأخذ على ذلك، في باش. الميزات: يتخطى الملفات غير العادية؛ يتعامل بشكل صحيح مع الملفات ذات الأحرف الغريبة (أي مسافات) بأسمائها؛ يتعامل مع أسماء الملفات غير المستدامة؛ يتخطى الملفات الموجودة بالفعل، بحيث يمكن تشغيلها مرارا وتكرارا (على الرغم من تعديل الملفات بين التشغيل، فهي تضيف التجزئة الجديدة بدلا من استبدالها القديمة). كتبت ذلك باستخدام MD5 -Q كدالة تجزئة؛ يجب أن تكون قادرا على استبدال هذا بأي شيء آخر، طالما أنه يخرج فقط التجزئة، وليس شيئا مثل FileName => Hash.

find -x . -type f -print0 | while IFS="" read -r -d $'\000' file; do
    hash="$(md5 -q "$file")" # replace with your favorite hash function
    [[ "$file" == *."$hash" ]] && continue # skip files that already end in their hash
    dirname="$(dirname "$file")"
    basename="$(basename "$file")"
    base="${basename%.*}"
    [[ "$base" == *."$hash" ]] && continue # skip files that already end in hash + extension
    if [[ "$basename" == "$base" ]]; then
            extension=""
    else
            extension=".${basename##*.}"
    fi
    mv "$file" "$dirname/$base.$hash$extension"
done

في sh أو bash، نسختان. يحد المرء نفسه الملفات مع ملحقات ...

hash () {
  #openssl md5 t.sh | sed -e 's/.* //'
  whirlpool "$f"
}

find . -type f -a -name '*.*' | while read f; do
  # remove the echo to run this for real
  echo mv "$f" "${f%.*}.whirlpool-`hash "$f"`.${f##*.}"
done

اختبارات...

...
mv ./bash-4.0/signames.h ./bash-4.0/signames.whirlpool-d71b117a822394a5b273ea6c0e3f4dc045b1098326d39864564f1046ab7bd9296d5533894626288265a1f70638ee3ecce1f6a22739b389ff7cb1fa48c76fa166.h
...

وهذا الإصدار الأكثر تعقيدا يعالج جميع الملفات العادي، مع أو بدون امتدادات، مع أو بدون مسافات وأحرف غريبة، إلخ، إلخ ...

hash () {
  #openssl md5 t.sh | sed -e 's/.* //'
  whirlpool "$f"
}

find . -type f | while read f; do
  name=${f##*/}
  case "$name" in
    *.*) extension=".${name##*.}" ;;
    *)   extension=   ;;
  esac
  # remove the echo to run this for real
  echo mv "$f" "${f%/*}/${name%.*}.whirlpool-`hash "$f"`$extension"
done

دوامة ليست تجهيز شائع جدا. ربما عليك تثبيت برنامج لحسابه. على سبيل المثال ديبيان / أوبونتو تشمل حزمة "دوامة". يطبع البرنامج تجزئة ملف واحد بحد ذاته. يبين Whirlpool APT-Cache البحث أن بعض الحزم الأخرى تدعمها، بما في ذلك MD5DEEP مثيرة للاهتمام.

ستفشل بعض الأشخاص السابقين في أسماء الملفات مع مسافات فيها. إذا كانت هذه هي الحالة، لكن ملفاتك لا تحتوي على أي خطوط جديدة في اسم الملف، فيمكنك استخدام n بأمان كمسؤول.


oldifs="$IFS"
IFS="
"
for i in $(find -type f); do echo "$i";done
#output
# ./base
# ./base2
# ./normal.ext
# ./trick.e "xt
# ./foo bar.dir ext/trick' (name "- }$foo.ext{}.ext2
IFS="$oldifs"

حاول دون تحديد IFS لمعرفة سبب أهمية ذلك.

كنت سأحاول شيئا مع IFS = "."؛ العثور على -print0 | أثناء قراءة مجموعة، تنقسم على "." الشخصيات، لكني عادة لا تستخدم متغيرات الصفيف. لا توجد طريقة سهلة أرى في صفحة الرجل لإدراج التجزئة كمؤشر صفيف آخر ثاني، وادفع العنصر الأخير (ملحق الملف، إذا كان لديه واحد.) في أي وقت متغيرات صفيف باش تبدو مثيرة للاهتمام، وأنا أعلم حان الوقت لفعل ما أقوم به في بيرل بدلا من ذلك! انظر gotchas لاستخدام القراءة:http://tldp.org/ldp/abs/html/gothas.html#badread0.

قررت استخدام تقنية أخرى أحبها: ابحث عن -EXEC SH -C. إنه الأكثر أمانا، لأنك لا تحليل أسماء الملفات.

هذا ينبغي أن تفعل خدعة:


find -regextype posix-extended -type f -not -regex '.*\.[a-fA-F0-9]{128}.*'  \
-execdir bash -c 'for i in "${@#./}";do 
 hash=$(whirlpool "$i");
 ext=".${i##*.}"; base="${i%.*}";
 [ "$base" = "$i" ] && ext="";
 newname="$base.$hash$ext";
 echo "ext:$ext  $i -> $newname";
 false mv --no-clobber "$i" "$newname";done' \
dummy {} +
# take out the "false" before the mv, and optionally take out the echo.
# false ignores its arguments, so it's there so you can
# run this to see what will happen without actually renaming your files.

-EXDIR BASH -C 'CMD' DUMMY {} + يحتوي على دمية هناك لأن Arg arm الأول بعد أن يصبح الأمر بعد 0 دولارا أمريكيا في المعلمات الموضعية لشل، وليس جزءا من "$ @". يمكنني استخدام Execdir بدلا من EXEC لذلك، لذا ليس علي التعامل مع أسماء الدليل (أو إمكانية تجاوز PATH_MAX للأقسام المتداخلة بأسماء طويلة، عندما تكون أسماء الملفات الفعلية قصيرة بما فيه الكفاية.)

-NOT -REGEX يمنع هذا من تطبيقه مرتين على نفس الملف. على الرغم من وجود دوامة طويلة للغاية، فإن MV يقول اسم الملف لفترة طويلة جدا إذا قمت بتشغيله مرتين بدون هذا الشيك. (على نظام ملفات XFS.)

الملفات مع عدم وجود تمديد الحصول على Basename.hash. اضطررت للتحقق خصيصا لتجنب إلحاق زائدة.، أو الحصول على الاسم الباسل كملحق. $ {} في السلسلة بأكملها للملفات بدون امتداد.

MV - No-clobber قد يكون ملحق غنو. إذا لم يكن لديك GNU MV، فقم بعمل شيء آخر إذا كنت ترغب في تجنب حذف الملفات الموجودة (على سبيل المثال، فأنت تعمل مرة واحدة، تتم إضافة بعض من نفس الملف إلى الدليل بأسمائها القديمة؛ لقد قمت بتشغيلها مرة أخرى.) Otoh، إذا كنت تريد هذا السلوك، فقط خلعها.

يجب أن يعمل الحل الخاص بي حتى عندما تحتوي أسماء الملفات على خط جديد (يمكن أن تعرف!)، أو أي شخصية محتملة أخرى. سيكون أسرع وأسهل في بيرل، لكن طلبت قذيفة.

حل Wallenborn لجعل ملف واحد مع كل المجموع الاختباري (بدلا من إعادة تسمية الأصل) جيد جدا، ولكنه غير فعال. لا تقم بتشغيل MD5SUM مرة واحدة لكل ملف، تشغيله على أكبر عدد ممكن من الملفات مرة واحدة على سطر الأوامر:

البحث عن Dir -Type F -Print0 | xargs -0 md5sum> dir.md5 أو مع gnu find، تم بناء xargs (لاحظ + بدلا من "؛ ') find dir -type f -exec md5sum {} +> dir.md5

إذا كنت تستخدم فقط Find -Print | Xargs -d ' n'، سيتم ثمل بأسماء الملفات مع علامات اقتباس فيها، لذلك كن حذرا. إذا كنت لا تعرف الملفات التي قد تديرها يوما ما هذا البرنامج النصي، فحاول دائما استخدام Print0 أو -EXEC. هذا هو esp. صحيح إذا تم توفير أسماء الملفات من قبل المستخدمين غير الموثوق بهم (أي يمكن أن يكون ناقلا مهزرا على الخادم الخاص بك.)

ردا على سؤالك المحدث:

إذا كان بإمكان أي شخص التعليق على كيفية تجنب البحث عن أدلة مخفية مع البرنامج النصي باش، فسيكون موضع تقدير كبير.

يمكنك تجنب الدلائل المخفية مع البحث باستخدام

find -name '.?*' -prune -o \( -type f -print0 \)

-name '.*' -prune سوف تقليم "."، وتوقف دون القيام بأي شيء. : /

ما زلت أوصي بإصدار بيرل الخاص بي، رغم ذلك. لقد قمت بتحديثها ... ربما لا تزال بحاجة إلى تثبيت Digest :: Whollpool من CPAN، رغم ذلك.

جلالة الملك، مشكلة مثيرة للاهتمام.

جرب ما يلي (وظيفة MKTest هي فقط لاختبار - TDD ل BASH! :)

يحرر:

  • إضافة دعم تجزئة دوامة.
  • كود تنظيف
  • اقتباس أفضل من أسماء الملفات
  • يجب أن تعمل بناء جملة صفيف للاختبار - الآن مع معظم قذائف مثل Korn. لاحظ أن PDKSH لا يدعم: توسيع المعلمة المعلمة (أو بالأحرى أنه يعني شيئا آخر)

لاحظ أيضا أنه عندما يكون في وضع MD5، فشل في أسماء الملفات مع التجزئة التي يشبه الدوامة، وربما العكس.

#!/usr/bin/env bash

#Tested with:
# GNU bash, version 4.0.28(1)-release (x86_64-pc-linux-gnu)
# ksh (AT&T Research) 93s+ 2008-01-31
# mksh @(#)MIRBSD KSH R39 2009/08/01 Debian 39.1-4
# Does not work with pdksh, dash

DEFAULT_SUM="md5"

#Takes a parameter, as root path
# as well as an optional parameter, the hash function to use (md5 or wp for whirlpool).
main()
{
  case $2 in
    "wp")
      export SUM="wp"
      ;;
    "md5")
      export SUM="md5"
      ;;
    *)
      export SUM=$DEFAULT_SUM
      ;;
  esac

  # For all visible files in all visible subfolders, move the file
  # to a name including the correct hash:
  find $1 -type f -not -regex '.*/\..*' -exec $0 hashmove '{}' \;
}

# Given a file named in $1 with full path, calculate it's hash.
# Output the filname, with the hash inserted before the extention
# (if any) -- or:  replace an existing hash with the new one,
# if a hash already exist.
hashname_md5()
{
  pathname="$1"
  full_hash=`md5sum "$pathname"`
  hash=${full_hash:0:32}
  filename=`basename "$pathname"`
  prefix=${filename%%.*}
  suffix=${filename#$prefix}

  #If the suffix starts with something that looks like an md5sum,
  #remove it:
  suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{32}//'`

  echo "$prefix.$hash$suffix"
}

# Same as hashname_md5 -- but uses whirlpool hash.
hashname_wp()
{
  pathname="$1"
  hash=`whirlpool "$pathname"`
  filename=`basename "$pathname"`
  prefix=${filename%%.*}
  suffix=${filename#$prefix}

  #If the suffix starts with something that looks like an md5sum,
  #remove it:
  suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{128}//'`

  echo "$prefix.$hash$suffix"
}


#Given a filepath $1, move/rename it to a name including the filehash.
# Try to replace an existing hash, an not move a file if no update is
# needed.
hashmove()
{
  pathname="$1"
  filename=`basename "$pathname"`
  path="${pathname%%/$filename}"

  case $SUM in
    "wp")
      hashname=`hashname_wp "$pathname"`
      ;;
    "md5")
      hashname=`hashname_md5 "$pathname"`
      ;;
    *)
      echo "Unknown hash requested"
      exit 1
      ;;
  esac

  if [[ "$filename" != "$hashname" ]]
  then
      echo "renaming: $pathname => $path/$hashname"
      mv "$pathname" "$path/$hashname"
  else
    echo "$pathname up to date"
  fi
}

# Create som testdata under /tmp
mktest()
{
  root_dir=$(tempfile)
  rm "$root_dir"
  mkdir "$root_dir"
  i=0
  test_files[$((i++))]='test'
  test_files[$((i++))]='testfile, no extention or spaces'

  test_files[$((i++))]='.hidden'
  test_files[$((i++))]='a hidden file'

  test_files[$((i++))]='test space'
  test_files[$((i++))]='testfile, no extention, spaces in name'

  test_files[$((i++))]='test.txt'
  test_files[$((i++))]='testfile, extention, no spaces in name'

  test_files[$((i++))]='test.ab8e460eac3599549cfaa23a848635aa.txt'
  test_files[$((i++))]='testfile, With (wrong) md5sum, no spaces in name'

  test_files[$((i++))]='test spaced.ab8e460eac3599549cfaa23a848635aa.txt'
  test_files[$((i++))]='testfile, With (wrong) md5sum, spaces in name'

  test_files[$((i++))]='test.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b798d369352a63c3d.txt'
  test_files[$((i++))]='testfile, With (wrong) whirlpoolhash, no spaces in name'

  test_files[$((i++))]='test spaced.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b798d369352a63c3d.txt']
  test_files[$((i++))]='testfile, With (wrong) whirlpoolhash, spaces in name'

  test_files[$((i++))]='test space.txt'
  test_files[$((i++))]='testfile, extention, spaces in name'

  test_files[$((i++))]='test   multi-space  .txt'
  test_files[$((i++))]='testfile, extention, multiple consequtive spaces in name'

  test_files[$((i++))]='test space.h'
  test_files[$((i++))]='testfile, short extention, spaces in name'

  test_files[$((i++))]='test space.reallylong'
  test_files[$((i++))]='testfile, long extention, spaces in name'

  test_files[$((i++))]='test space.reallyreallyreallylong.tst'
  test_files[$((i++))]='testfile, long extention, double extention,
                        might look like hash, spaces in name'

  test_files[$((i++))]='utf8test1 - æeiaæå.txt'
  test_files[$((i++))]='testfile, extention, utf8 characters, spaces in name'

  test_files[$((i++))]='utf8test1 - 漢字.txt'
  test_files[$((i++))]='testfile, extention, Japanese utf8 characters, spaces in name'

  for s in . sub1 sub2 sub1/sub3 .hidden_dir
  do

     #note -p not needed as we create dirs top-down
     #fails for "." -- but the hack allows us to use a single loop
     #for creating testdata in all dirs
     mkdir $root_dir/$s
     dir=$root_dir/$s

     i=0
     while [[ $i -lt ${#test_files[*]} ]]
     do
       filename=${test_files[$((i++))]}
       echo ${test_files[$((i++))]} > "$dir/$filename"
     done
   done

   echo "$root_dir"
}

# Run test, given a hash-type as first argument
runtest()
{
  sum=$1

  root_dir=$(mktest)

  echo "created dir: $root_dir"
  echo "Running first test with hashtype $sum:"
  echo
  main $root_dir $sum
  echo
  echo "Running second test:"
  echo
  main $root_dir $sum
  echo "Updating all files:"

  find $root_dir -type f | while read f
  do
    echo "more content" >> "$f"
  done

  echo
  echo "Running final test:"
  echo
  main $root_dir $sum
  #cleanup:
  rm -r $root_dir
}

# Test md5 and whirlpool hashes on generated data.
runtests()
{
  runtest md5
  runtest wp
}

#For in order to be able to call the script recursively, without splitting off
# functions to separate files:
case "$1" in
  'test')
    runtests
  ;;
  'hashname')
    hashname "$2"
  ;;
  'hashmove')
    hashmove "$2"
  ;;
  'run')
    main "$2" "$3"
  ;;
  *)
    echo "Use with: $0 test - or if you just want to try it on a folder:"
    echo "  $0 run path (implies md5)"
    echo "  $0 run md5 path"
    echo "  $0 run wp path"
  ;;
esac

باستخدام ZSH:

$ ls
a.txt
b.txt
c.txt

السحر:

$ FILES=**/*(.) 
$ # */ stupid syntax coloring thinks this is a comment
$ for f in $FILES; do hash=`md5sum $f | cut -f1 -d" "`; mv $f "$f:r.$hash.$f:e"; done
$ ls
a.60b725f10c9c85c70d97880dfe8191b3.txt
b.3b5d5c3712955042212316173ccf37be.txt
c.2cd6ee2c70b0bde53fbe6cac3c8b8bb1.txt

تفكيك سعيد!

تحرير: ملفات مضافة في الدلائل الفرعية والاقتباسات حولها mv جدال

روبي:

#!/usr/bin/env ruby
require 'digest/md5'

Dir.glob('**/*') do |f|
  next unless File.file? f
  next if /\.md5sum-[0-9a-f]{32}/ =~ f
  md5sum = Digest::MD5.file f
  newname = "%s/%s.md5sum-%s%s" %
    [File.dirname(f), File.basename(f,'.*'), md5sum, File.extname(f)]
  File.rename f, newname
end

مقابض أسماء الملفات التي لها مسافات، لا تمديد، والتي تم بالفعل التغليف بالفعل.

يتجاهل الملفات والدلائل المخفية - إضافة File::FNM_DOTMATCH كما الحجة الثانية من glob إذا كان ذلك مطلوبا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top