ディレクトリタイムスタンプを最後に変更されたファイルに設定(再帰的)

ディレクトリタイムスタンプを最後に変更されたファイルに設定(再帰的)

埋め込みファイルの変更時間を反映するように、ディレクトリとそのディレクトリ内のすべてのサブフォルダのタイムスタンプをどのように変更できますか?

たとえば、次のディレクトリ構造を使用します。

[Jan 9]  root
├── [Jan 3]  file1
├── [Jan 7]  file2
├── [Jan 6]  sub1
│   ├── [Jan 2]  file3
│   └── [Jan 1]  file4
└── [Jan  4] sub2
    └── [Jan 8]  file5

以下は、このコンテンツを生成する1行のコードです。

mkdir -p root/sub1 root/sub2 && touch -d '2018-01-08' root/sub2/file5 && touch -d '2018-01-04' root/sub2/ && touch -d '2018-01-01' root/sub1/file4 && touch -d '2018-01-02' root/sub1/file3 && touch -d '2018-01-06' root/sub1/ && touch -d '2018-01-07' root/file2 && touch -d '2018-01-03' root/file1 && touch -d '2018-01-09' root/

それはリストできるtree -D

3つのディレクトリのタイムスタンプを次のように変更したいと思います。

[Jan 8]  root
├── [Jan 3]  file1
├── [Jan 7]  file2
├── [Jan 2]  sub1
│   ├── [Jan 2]  file3
│   └── [Jan 1]  file4
└── [Jan 8]  sub2
    └── [Jan 8]  file5

メモ:

  • ディレクトリの現在のタイムスタンプは完全に無視され、新しいタイムスタンプはコンテンツのみに基づいて設定されます。
  • タイムスタンプは、親ディレクトリの複数のレベルまで表示されます。

私がこれを行う理由は、rsyncを使用してディレクトリをコピーするためです。ディレクトリはgitにチェックインされ、リポジトリがチェックアウトされたどこでもrsyncdすることができます。 rsync がどこでも一貫して冪等性を持つようにするには、すべての項目のタイムスタンプと権限がわかっていることを確認する必要があります。すでにgitにコミットされている時間に基づいてファイルのタイムスタンプを設定するスクリプトがあります。また、すべてのファイルとディレクトリの権限を既知の状態に設定するスクリプトもあります。私が持っている唯一の問題は、ファイルから親ディレクトリへのバブリングタイムスタンプです。

ディレクトリの内容のタイムスタンプに基づいてディレクトリのタイムスタンプを設定するためにコマンドラインで実行できる1行または短いスクリプトが必要です。

答え1

私が見過ごしていない限り(これを広くテストしていません):

find . -depth -type d -execdir \
    sh -c 'touch "$PWD/$0" -r "$PWD/$0/$( ls -t "$PWD/$0" | head -n 1 )"' \
    {} \;

できること:

  • すべてのサブディレクトリを探します。
  • 名前を学ぶ
  • を使用して、ls -t日付/時刻の逆順に内容を一覧表示し、最初の項目を取得します。
  • touch最新のオブジェクトをタイムスタンプとしてディレクトリをタッチする場合

正しい日付/時刻「バブリング」にこの-depthオプションを使用すると、サブディレクトリが処理されます。今後現在のサブディレクトリ(「深さ優先」)。サブディレクトリが処理され、対応する日付/時刻が更新されると、現在のディレクトリが処理されます(サブディレクトリの1つのタイムスタンプを継承できます)。

答え2

Zshが利用可能な場合は、次のようにしたいことができるようです。

$ mkdir -p x/y; touch x/y/a x/b; sleep .1; touch x/y/a; sleep .1; touch x/b
$ find -depth -type d -execdir zsh -c 'touch "$1" -r "$1"/*(om[1])' zsh {} \;

GNU findを使用して、出力が./x/b最新で、そのタイムスタンプがからコピーされているかどうかを./xテストします.

$ find -printf "%TT %p\n"  | sort -n
15:16:24.0182222830 ./x/y
15:16:24.0182222830 ./x/y/a
15:16:24.1222150510 .
15:16:24.1222150510 ./x
15:16:24.1222150510 ./x/b

通常、ファイル名にスペース/改行などがある場合は、タイムスタンプで最新のファイルを見つけるのが少し面倒です。 (望むよりバッシュFAQ 099.) Zshを使うとかなり簡単にできますが、上記のヒントはコマンドライン引数で最近変更されたファイルを参照できますか?

-depthfindバブリングが実際に機能するように、サブディレクトリが親ディレクトリの前に処理されるように指示します。

タイムスタンプがバブリングされると計算される必要があるため、実際にディレクトリのタイムスタンプを完全に無視するわけではありません。ファイルを含むディレクトリはファイルからタイムスタンプを取得しますが、ディレクトリは元のタイムスタンプを保持し、対応するタイムスタンプが表示されます。この問題を解決する価値があるかどうかはわかりません。

答え3

Linuxでは、これは私にとってうまくいきます。

while IFS=$'\t' read -r -a REPLY; do
  echo touch -c -h -d@"${REPLY[0]}" "${REPLY[1]}"
  touch -c -h -d@"${REPLY[0]}" "${REPLY[1]}"
done < <(du "${1:-.}" --time-style=+%s --time | cut -f 2-)

関連情報