ディレクトリの最後に変更された日時を、最後に変更されたファイルと同じ日時として再帰的にタッチする


1

以前は、次のコマンドを使用して、サーバーdos2unix上のすべての*.phpファイルを再帰的に実行しました。

find . -name '*.php' -type f -exec dos2unix --keepdate {} +

によって処理されdos2unixたすべてのファイルの最終変更日時は変更されませんでしたが、そのようなファイルを含む親ディレクトリの最終変更日時は、コマンドを実行した時刻に更新されたままです。

ライナーを1つ使用して、ディレクトリを再帰的にタッチし、ディレクトリの最終変更日時をディレクトリ内の最終変更ファイルと同じ日時に設定するにはどうすればよいですか?

これから:

dir_a/               2013-05-13 14:05
   abc.php           2012-09-01 12:34
   def.php           2012-09-15 23:45
   dir_b/            2013-05-13 14:05
       uvw.php       2012-10-01 01:23
       xyz.php       2012-10-08 09:10

これに:

dir_a/               2012-09-15 23:45
   abc.php           2012-09-01 12:34
   def.php           2012-09-15 23:45
   dir_b/            2012-10-08 09:10
       uvw.php       2012-10-01 01:23
       xyz.php       2012-10-08 09:10

サーバーはCentOS 5.6で実行されています。

回答:


2

たぶんこのようなもの:

find . -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";if [ -n "$file" ]; then touch "$dir" -mr "$file"; fi; done

説明:

あるファイルから別のファイルに変更日を渡すには、次を実行する必要があります。

touch file1 -mr file2

まず、サブディレクトリを見つける必要があります。

find . -type d -print0 | while read -r -d '' dir; do echo "$dir"; done

この場合、-execオプションは複雑すぎるため、読み取り中のアプローチを使用します。レコード区切りとしてfindwithを使用NULして出力をパイプし、readその上で分割するように指示する必要があります(-d '')。

最終変更日を持つファイルを見つけるには、次を使用できます。

find ./  -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-

それを組み合わせる:

find . -mindepth 1  -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";echo "$dir <- $file"; done

最後に、タッチを追加します。

find . -mindepth 1  -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";touch "$dir" -mr "$file"; done

-mindepthここでオプションを使用するのは、日付を読み取るファイルがないdir_aを含むディレクトリからこのコマンドを起動したためです。この問題を解消するには、if-elseを使用して、存在しないファイルから時間を読み取らないようにします。

find . -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f  -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";if [ -n "$file" ]; then touch "$dir" -mr "$file"; fi; done

回答のおかげで、これはほぼ完璧に機能touchし、ファイル名にスペースが含まれるファイルから最終変更日時を取得しようとしたときにのみ失敗しました。
フィル

2
@Philええ、それはおそらく$(find …)中央の(壊れた)構造のためです。を二重引用符で囲んで交換し-f2てみてください。-f2-$()
slhck

@slhckこれはの問題を修正しtouchましたfindが、ディレクトリ名にスペースが含まれていると失敗することに気付きました。これも修正する方法を指摘できますか?ありがとう。
フィル

1
@Phil True、それfind … | whileも壊れていた。で呼び出して、内側のループで-print0 | while read … -d ''引用する必要があり"$dir"ます。
slhck

1

これはそれを行う必要があります:

find -mindepth 1 -type d -print0 | while IFS= read -r -d '' dir; do 
 time=$(
  find "$dir" -mindepth 1 -maxdepth 1 -type f -exec stat -c %Y {} \; | 
  sort -nk2 | tail -n 1
 ); touch -d @$time "$dir"; 
 done

説明:

  • find -mindepth 1 -type d | while IFS= read -r -d '' dir; do:現在のディレクトリ(-mindepth 1)を除くすべてのディレクトリを反復処理します。フラグとフラグを使用してwhileループを使用するIFS=と、-rコマンドがスペースまたは改行(\n)またはその他の奇妙な文字を含むディレクトリ名で動作することを確認できます。-d ''ヌル文字にセット区切り、それは、findので作業できるように、-print0オプション。

  • find $dir -mindepth 1 -maxdepth 1 -type f -exec stat --printf="%Y\n" {} \;:各ディレクトリのすべてのファイルを見つけて$dir実行stat、エポック以降の変更日を秒単位で出力します。

  • sort -nk2 | tail -n 1 :これは日付を並べ替え、最新のものを保持します。最後の2つの部分を組み合わせて、変数に保存されている最新のファイルの変更時間を示します$time

  • touch -d @$time $dir:ここでは、touchコマンドを使用して各ディレクトリの変更時刻を設定します。@伝えることがありtouch、我々は再エポックからの秒数で時間を与えていること、これは標準GNU coreutilsの機能


答えてくれてありがとう、しかしtouch: invalid date format `@'これを実行すると多くのエラーが出ます。
フィル

@Phil 実行$time後に何かが含まれているかどうかを確認し$(find …)ます。$timeが空の場合にのみ、このエラーが発生します。
slhck

@Philは、ファイルを含まないディレクトリがある場合に発生します。の値は各ディレクトリの$time最新のファイルに依存するため、ディレクトリにファイルが含まれていない場合は空になります。ただし、これは問題ではないはずです。これらのメッセージを無視してください。ファイルのないディレクトリは気にしません。
テルドン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.