一時ファイルを管理し、アーカイブとクリーンアップを自動化するBashスクリプトを作成する方法は?

一時ファイルを管理し、アーカイブとクリーンアップを自動化するBashスクリプトを作成する方法は?

Bashスクリプトを使用してLinux環境で一時ファイルを管理するソリューションが必要です。

  • ファイルがルートフォルダにあり、10日間変更されていないままになっている場合は、ファイルをサブフォルダに移動してタイムスタンププレフィックス(「2022-01-01_filename」など)を追加します。
  • 以前に移動したファイルが変更された場合は、タイムスタンププレフィックスなしでルートフォルダに返す必要があります。
  • 最後の変更日から90日以内に変更されていないファイルを削除します。

私が試したことは次のとおりです。いくつかのエラーが発生しましたが、そのエラーを修正した後は何も起こらないようです。とにかく、何時間も悩んだ最後に変更された日付で並べ替えることができるファイルマネージャを使用することをお勧めします。

#!/bin/bash

FOLDER="$HOME/tmp"
SUBFOLDER="$HOME/tmp/old"

[ ! -d "$FOLDER" ] && mkdir -p "$FOLDER"
[ ! -d "$SUBFOLDER" ] && mkdir -p "$SUBFOLDER"

find "$FOLDER" -maxdepth 1 -type f -mtime +10 -exec sh -c '
  for file do
    timestamp=$(date +"%Y-%m-%d")
    new_filename="$SUBFOLDER/$timestamp_$(basename "$file")"
    mv "$file" "$new_filename"
  done' sh {} +

find "$SUBFOLDER" -type f -mtime -10 -exec sh -c '
  for file do
    new_filename="$FOLDER/${file#*_}"
    mv "$file" "$new_filename"
  done' sh {} +

find "$SUBFOLDER" -type f -mtime +90 -exec rm {} \;
chmod +x /path/to/script.sh
crontab -e
0 0 * * * /path/to/script.sh

ベストアンサー1

このコードにはいくつかの問題があります(一部はマイナーな問題です)。

  • FOLDERこれらの変数と変数は環境にエクスポートされないため、SUBFOLDERから始まる呼び出しでは使用できません。shfind
  • ディレクトリは~/tmpプライベートでなければなりませんmkdir -m 700 ~/tmp
  • これは[ -d ... ] || mkdir ...典型的なTOCTOU競争モードです。事前検査は不要ですmkdir -p
  • $(...)任意のファイル名で使用できないように末尾の改行を削除します。
  • エラー処理は行いません。エラーがある場合は、少なくとも終了ステータスを報告できます。
  • 繰り返すたびに、今日の日付を計算する必要はありません。
  • これは-mtime +1011日以上のファイルに適用され、-mtime -1010日未満のファイルに適用されるため、24時間期間のファイル(10〜11日前に最後に変更されたファイル)はここでは扱いません。
  • find2番目のコマンドは、名前に文字が含まれていないか含まれているファイルがある場合は、期待どおりに機能しません。たとえば、もしそうであれば、次のようになり、for、thenが与えられます。_$SUBFOLDER_$file/home/you/tmp/old/old-file$new_file_name/home/you/tmp//home/you/tmp/old/old-file/home/your_home/tmp/old/2023-10-01_file/home/your_home/tmp/home/tmp/old/2023-10-01_file
  • mvすでに存在するターゲットファイル(ディレクトリまたはディレクトリへのシンボリックリンク)が処理されないなどの競合。
  • -maxdepth標準find述語ではありません。

そしてzsh

#! /bin/zsh -
autoload zmv || exit
mkdir -pm 700 -- ~/tmp && cd -P -- ~/tmp && mkdir -p old || exit

today=${(%):-%D{%F}} ret=0

# move 10 day old or older regular files
zmv '*(#qND.m+9)' 'old/${today}_$f' || ret=$?

# move back regular files that are less than 10 day old.
zmv 'old/<1900-2100>-<1-12>-<1-31>_(*)(#qND.m-10)' '$1' || ret=$?

# remove regular files that are 91 day old or older
rm -f old/*(ND.m+90) || ret=$?

exit $ret

shbashにはshよりも有用な拡張機能がないため、bashを使用する必要はありません。これはここで役に立ちます。自分のコードにbashに関連するものはなく、代わりに.shスクリプトに拡張機能を提供します.bash。)

#! /bin/sh -
mkdir -pm 700 -- ~/tmp && cd -P -- ~/tmp && mkdir -p old || exit

TODAY=$(date +%Y-%m-%d) || exit
export TODAY

ret=0
# move 10 day old or older regular files
find . ! -name . -prune -type f -mtime +9 -exec sh -c '
  ret=0
  for file do
    file=${file#./}
    mv -i -- "$file" "old/${TODAY}_$file" || ret=$?
  done
  exit "$ret"' {} + || ret=$?

# move back regular files that are less than 10 day old.
LC_ALL=C find old/. ! -name . -prune \
  -name '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_*' \
  -type f -mtime -10 -exec sh -c '
  ret=0
  for file do
    new=${file#*/./}
    new=${new#*_}
    if [ -d "$new" ]; then
      printf>&2 "%s\n" "$new is a directory"
      ret=1
    else
      mv -i -- "$file" "$new" || ret=$?
    fi
  done
  exit "$ret"' {} + || ret=$?

# remove regular files that are 91 day old or older
find old/ -mtime +90 -type f -exec rm -f {} + || ret=$?

exit "$ret"

-i競合を処理するためにここで使用されるオプションmv。対話的に実行すると、ファイルを上書きするとメッセージが表示されます。cronstdin isで実行すると上書きは/dev/null拒否されますが、残念ながら失敗した場合は返されません。-nGNUオプションも参照してくださいmv(ただし失敗も返しません)。

ディレクトリ(またはディレクトリへのシンボリックリンク)として存在する場合、名前は変更されずに移動mv a bされます。したがって、事前に確認してください。しかし、これはまたTOCTOU競争を導入します。 GNUの実装には便利なオプションがあります。の衝突処理において同様の競争条件があります。 GNUシステムに渡すと、これらの問題の少なくとも一部を軽減するのに役立ちます。abbb[ -dmv-Tzshzmv-o -nTzmv

(注:私はコードをテストしていません。)

おすすめ記事