※当サイトの記事には、広告・プロモーションが含まれます。

Linuxの標準コマンドでファイル検知は無理そうなので、無限ループでファイルの変更を検知してみる

www.itmedia.co.jp

⇧ システムが動くことは当たり前じゃないことを身をもって証明してくれた「独立行政法人情報処理推進機構IPA: Information-technology Promotion Agency, Japan)」さん。

システムが正常稼働することに対する不確実性をもっと、周知して欲しいですな。

www.ipa.go.jp

⇧ 原因について判明したら、改善前・改善後をしっかり解説して欲しいんですけどね。

ちなみに、

www.ipa.go.jp

⇧ Webサイトリニューアルでも大失敗しているので、システムの開発や保守・運用を正常に遂行することの難しさについては、改めて周知して欲しいですな。

blog1.mammb.com

⇧ 上記サイト様によりますと、2014年頃のアメリカの調査によると、開発プロジェクトのおよそ30%あまりについては頓挫したと...

計画通りに完遂した開発プロジェクトは、およそ16%らしい。

medium.com

⇧ 上記サイト様によりますと、アメリカと違って、日本は頓挫する開発プロジェクトはほとんど無いということでしょうか。

ただ、およそ69%は失敗と見なさていると。

何と言うか、調査による統計が一般的に当てはまるとするならば、計画通りになるのはほとんど奇跡と言っても過言ではないということでしょうか。

エンジニア、非エンジニアに関わらず、遍く行き届いて欲しい情報ですな。

Linuxの標準コマンドでファイル検知は無理そう

ネットの情報を見た感じ、

www.nminoru.jp

ittrip.xyz

⇧ inotify-toolsってものをインストールする必要があるっぽいので、Linuxの標準コマンドでのファイル検知は無理っぽい。

C言語とかできるなら話は別なのかもしらんけど...

Linuxの標準コマンドでファイル検知は無理そうなので、無限ループでファイルの変更を検知してみる

とりあえず、外部からファイルが転送されてきてファイルが更新されることを想定しているのですが、

qiita.com

⇧ ファイルサイズとか取得する感じになるんかな。

ex1.m-yabe.com

⇧ ファイルの更新日の方が良いかしら?

最終的には、ファイルの中身に特定の文言が書き込まれていたら、処理が終了するような無限ループの中で、ファイルの変更を確認していく感じかしら。

シェルスクリプトにしてみた。

#!/usr/bin/env bash

# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# ■  関数定義
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# ■  Description(説明):
# ■    現在の年月日をepoch秒で取得する
# ■  Arguments(引数):
# ■    なし
# ■  Returns(戻り値):
# ■    epoch秒(現在の年月日)
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
function epoch_time_seconds() {
  date +'%s'
}

# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# ■  Description(説明):
# ■    epoch秒(現在の年月日)をYYYY-MM-DD hh:mm:ssのフォーマットに変換
# ■  Arguments(引数):
# ■    $1 epoch秒(現在の年月日)
# ■  Returns(戻り値):
# ■    現在の年月日(YYYY-MM-DD hh:mm:ssのフォーマット)
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
function epoch_to_datetime() {
  date -d "@${1}" +"${2:-%F %T}"
 
}

# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# ■  Description(説明):
# ■    epoch秒(ファイルの更新日)を取得する
# ■  Arguments(引数):
# ■    $1 ファイルのパス
# ■  Returns(戻り値):
# ■    epoch秒(ファイルの更新日)
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
function epoch_file_modified() {
  local _TARGET_FILE_PATH="$1"
  ls -l --time-style='+%s' "${_TARGET_FILE_PATH}" | awk '{print $6}'

}

# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# ■  変数定義
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# 監視対象のファイルの配置パス
readonly TARGET_FILE_DIR=/home/ts0818/test_watch

# 監視対象のファイル名
TARGET_FILE_NAME=progress.txt

# 監視対象のファイルまでのパス
TARGET_FILE_PATH=${TARGET_FILE_DIR}/${TARGET_FILE_NAME}

# ログ
LOG_DIR=/home/ts0818/test_watch/log
LOG_FILE=test_watch.log
LOG_PATH=${LOG_DIR}/${LOG_FILE}

# ディレクトリの作成
mkdir -p ${TARGET_FILE_DIR}
mkdir -p ${LOG_DIR}

# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# ■  主処理
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# 初期化
cat /dev/null > ${TARGET_FILE_PATH}

# タイムアウト値
TIMEOUT_SECONDS=60

# 処理開始時間
start_time=$(epoch_time_seconds)

# 経過時間
passed_time=0

# ファイル更新日
CURRENT_MODIFIED_DATETIME=epoch_file_modified "${TARGET_FILE_PATH}"

# ファイルの内容
CURRENT_TEXT=(cat "${TARGET_FILE_PATH}")

# 無限ループ
while true
do
  if [ ${passed_time} -eq 0 ]; then
    echo '['"date +%Y%m%d-%H%M%S"']start while loop.'
 
  fi
  
  # ファイルの変更を検知
  MODIFIED_DATETIME=(epoch_file_modified "${TARGET_FILE_PATH}")
  if [ ${CURRENT_MODIFIED_DATETIME} -ne ${MODIFIED_DATETIME} ]; then
    # 標準出力で改行する
    echo;

    # ファイルの内容で追記されている部分を抽出し、標準出力に出力する
    diff --old-line-format='' --unchanged-line-format='' --new-line-format='%L' "${CURRENT_TEXT}" "${TARGET_FILE_PATH}"

    # ファイルの内容を変数に格納
    CURRENT_TEXT=$(cat "${TARGET_FILE_PATH}")
    
    # 最新の更新日を格納
    CURRENT_MODIFIED_DATETIME=${MODIFIED_DATETIME}
    
  fi
  
  # 全ての処理が終了したかどうかチェック
  grep "ALL FINISHED" ${TARGET_FILE_PATH} > /dev/null
  if [ $? = 0 ];then
    # 標準出力で改行する
    echo;
    
    # 全ての処理が完了した旨を標準出力に出力する
    echo '['"date +%Y%m%d-%H%M%S"']ALL FINISHED';
    
    # 無限ループを抜ける旨を標準出力に出力する
    echo '['"date +%Y%m%d-%H%M%S"']finish while loop.'
    
    # 無限ループを終了する
    break;

  fi
  
  # 進捗を出力
  echo "・"
  
  sleep 1

  # タイムアウト用の処理
  passed_time=$(( $(epoch_time_seconds) - ${start_time} ))
  echo "passed ${passed_time} seconds."
   
  # タイムアウトの場合
  if [ ${passed_time} -ge ${TIMEOUT_SECONDS} ]; then
    echo "over ${TIMEOUT_SECONDS} seconds. it's timeout."
    echo "break while loop."
    # 無限ループを終了する
    break;
 
  fi
  
done
echo '['"date +%Y%m%d-%H%M%S"']end all process.'

⇧ 動作確認していないけど、上記のような感じになるんかな。

仮想マシンLinux環境)が1台しか用意できないような場合は、

qiita.com

⇧ 上記サイト様にありますように、Windowsの.batファイルを用意すれば、動作確認できるかと。

気になるのは、外部から転送されてくるファイルの変更の扱いかな...

Linuxファイルシステムが、何をもってファイルが変更されたと認識するのかが分からんのよな...

例の如く、ChatGPTさんに聞いてみた。

Linuxファイルシステムでは、ファイルが変更されたことを検出するためにいくつかの方法があります。主な方法は以下の通りです。

  1. タイムスタンプ: ファイルのメタデータには、最終アクセス時刻、最終変更時刻、最終メタデータ変更時刻などのタイムスタンプが含まれています。これらのタイムスタンプを比較することで、ファイルが最後に変更されたかどうかを判断できます。
  2. ファイルシステムのイベント: ファイルが変更されると、ファイルシステムがイベントを生成する場合があります。これには、ファイルが作成、変更、削除されたことを示すイベントが含まれます。ファイルシステムイベントを監視することで、変更を検出することができます。
  3. チェックサムハッシュ値の比較: ファイルの内容が変更されると、チェックサムハッシュ値も変化します。定期的にファイルのハッシュ値を計算し、以前のハッシュ値と比較することで、ファイルの変更を検出することができます。
  4. インクリメンタルバックアップ: インクリメンタルバックアップでは、前回のバックアップ時点から変更されたファイルのみをバックアップします。この方法を使用すると、変更されたファイルを効率的に検出することができます。

⇧ ということらしい。

外部からファイルが転送されてきた場合、ファイルの内容が変更されていない場合でも、ファイルのメタデータ(タイムスタンプや拡張ファイル属性など)は変更される可能性があります。そのため、ファイルシステムメタデータの変更を検知してファイルが変更されたと判断することがあります。

ただし、ファイルの内容が変更されていない場合にもファイルが変更されたと判断されるかどうかは、ファイルシステムの具体的な実装や設定に依存します。一般的には、ファイルの内容が変更されていない場合でも、ファイルのメタデータが変更されるだけであれば、ファイルが変更されたとはみなされないことが多いです。

⇧ 要件次第ということかね。

今回は、

  • シェルスクリプトの実行において、排他制御は考慮不要
  • ファイルの内容が追記されてファイルが転送されてくる
  • ファイルの中身から処理の完了を判定できる

ということが分かっているけども、要件によっては、複雑な実装が必要になってくるかもしれないと。

Linux環境なので無限ループでなくとも、cronで定期的に実行でも良いかもしれんけど、タイムアウト処理とかの兼ね合いを考えると、無限ループの方が良いんかな...

毎度モヤモヤ感が半端ない...

今回はこのへんで。