膨大な数のファイルを日付構造のディレクトリ順に移動する

膨大な数のファイルを日付構造のディレクトリ順に移動する

このディレクトリには約 100 万個のファイルがあります。/home/username/images/

各ファイルの名前は次のように付けられ、012345678910(Place)_0_20120414185957_28841.jpgファイル名のタイムスタンプ部分は画像ごとに変わります。

以下のコードには、ファイルをこの日付構造に並べ替え/移動するコードが含まれています。/home/username/sorted/2012/04/14/18/name_of_file.jpg

小さなサンプルファイルでは問題なく動作しますが、巨大なディレクトリでは出力後にPuTTYターミナルが切断されます。

Directory $newdir does not exist.  Creating same.

常にエラー コードで終了する他のコードもありましたargument list too long

コードは次のとおりです:

#!/bin/bash
ALLFILES=(images/*)
for ((i=0; i<${#ALLFILES[*]}; i+=30000));
do
    set $(echo "${ALLFILES[@]:i:30000}" | awk -F_ '{print $1, $2, $3, $4, $5}')
    fullyear=$3
    year=$(echo $fullyear |cut -c1-4)
    month=$(echo $fullyear |cut -c5-6)
    day=$(echo $fullyear |cut -c7-8)
    hour=$(echo $fullyear |cut -c9-10)
    newdir=$(echo /home/username/sorted/$year/$month/$day/$hour/)
    if ! [ -d $newdir ]; then
        echo Directory $newdir does not exist.  Creating same.
        mkdir -p $newdir;
    fi
    mv "${ALLFILES[@]:i:30000}" $newdir;
done

大きなループを実行しているときに接続が維持されない理由について何か考えはありますか?

答え1

screen セッションで実行してみてください。または、別の構成を試してください。find + sed は純粋な bash よりもうまく機能すると思います。

find images/ -name "*.jpg" | sed 's%^[^_]*_[^_]*_\([0-9][0-9][0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\).*%mkdir -p "/home/username/sorted/\1/\2/\3/\4" \&\& mv "&" "/home/username/sorted/\1/\2/\3/\4/"%'

これは、sed make コマンドの実行方法を示すだけです。e最後に追加すると%、コマンドが強制的に実行されます。

find images/ -name "*.jpg" | sed 's%^[^_]*_[^_]*_\([0-9][0-9][0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\).*%mkdir -p "/home/username/sorted/\1/\2/\3/\4" \&\& mv "&" "/home/username/sorted/\1/\2/\3/\4/"%e'

ps. bashで使用する必要はありません

day=$(echo $fullyear |cut -c7-8)

Bash は以下なしでも独自に実行できますecho | cut:

day=${fullyear:6:2}

答え2

私は、ファイルが詰め込まれたディレクトリのルートでこのシェル スクリプトを使用して、それらすべてをyear/month-like 構造に移動しています。

#!/usr/bin/env bash

if [ ! $1 ]; then
    echo "Usage: ./pictures.sh jpg"
    exit 1
fi

for f in *."$1"; do
    FILENAME="$f"
    YEAR=`date -j -f "%s" $(stat -f "%m" "$FILENAME") +"%Y"`
    MONTH=`date -j -f "%s" $(stat -f "%m" "$FILENAME") +"%m_%B"`
    DEST="$YEAR/$MONTH"

    if [ ! -d "$DEST" ]; then
        mkdir -p "$DEST"
    fi

    echo "Moving $FILENAME to $DEST/$FILENAME ..."
    mv "$FILENAME" "$DEST/$FILENAME"
done

使用法: $ ./pictures.sh JPG*.JPG を正しい構造に移動します。

答え3

私も画像を日付構造のディレクトリに分類していますが、少し異なるアプローチをとっています。画像をYYYY-MMタイムスタンプに基づいてそれぞれのディレクトリに移動させたいのです。そこで、ls -l *.jpg > tmp.txt画像フォルダから始めて、これをループに送りtmp.txt、各ファイルのタイムスタンプを取得します。それ以外の方法でタイムスタンプを取得する方法は見つかりませんでした。

これが私のコードです:

#!/bin/bash
hostdir="/home/Photos/"
destdir="/tmp/sorted"

cd $hostdir

touch /tmp/tmpsort.txt
ls -l *.jpg > /tmp/tmpsort.txt

while read line
do
    filename=$(echo $line | awk '{print $8}')
    filedate=$(echo $line | awk '{print $6}')
    filedir=${filedate:0:7}

    if [ ! -d $destdir/$filedir ]; then
        mkdir -p $destdir/$filedir
    fi

    # Let's skip files that were already sorted from a previous run
    if [ ! -f $destdir/$filedir/$fiename ]; then
        cp $filename $destdir/$filedir/
    fi

done < /tmp/tmpsort.txt
rm /tmp/tmpsort.txt

並べ替える画像が何百万枚もあるわけではないので、もしあったとしたら、このコードの実行にはかなり時間がかかります。しかし、意図したとおりに動作します。

答え4

次のワンライナーは、変更時刻に基づいてファイルを正しいフォルダーに移動するシェル スクリプトを作成します。

find . -type f -not -name ".DS*" -exec stat -f "mkdir -p %Sm; mv \"%N\" %Sm" -t "%Y/%m/%d" {} \; > move.sh
sh move.sh

.DS* ファイルを除外しました (-not -name ".DS*") move.sh を実行する前に、不要なファイルを削除して編集できます。

関連情報