2016年11月24日

ファイルを再帰的に探索し更新時間でソートするワンライナー

本日、ぱぴろん(@papiron)さんの次のツイートを拝見して、 ls コマンドを用いる以外の方法はあるのだろうかと、少し疑問に感じた。

そこで、Twitter上で、自分なりに様々な手法を考えていたところ、ベテランシェル芸人の皆さんにアドバイスを頂いて、幾つか実現することが出来た。
自身のノウハウの蓄積として、また同様の操作が必要な人への参考として、ファイルを再帰的に探索し更新時間でソートするワンライナー方法を、記事としてまとめた。


実行環境

  • Arch Linux 4.8.8-2-ARCH

  • GNU bash 4.4.5

  • GNU coreutils 8.25-2

  • GNU findutils 4.6.0-2

  • sed (GNU sed) 4.2.2

  • gawk 4.1.4

下準備

まずは、実験用のデータを準備する。
/etc を丸ごと /tmp にコピーし、タイムスタンプをランダムに設定する。
折角なので、タイムスタンプのランダム設定も、ワンライナーでやってみる。

$ cp -r /etc /tmp 2>/dev/null
$ find /tmp/etc | wc -l | gawk -M 'BEGIN{srand()} {for(i=1;i<=$0;i++){printf("@%d\n",(rand()^2)*(10^10))}}' | xargs -I@ date -d @ +'%Y%m%d%H%M' | paste - <(find /tmp/etc -type f) | sed 's/.*/touch -t &/' | sh

以下、後半部分の解説。

#!/bin/bash

find /tmp/etc                                                                         | # /tmp/etc 以下の全ファイルを列挙
wc -l                                                                                 | # ファイル数を出力
gawk -M 'BEGIN{srand()} {for(i=1;i<=$0;i++){printf("@%d\n",(rand()^2)*(10^10))}}'     | # ランダムなUNIX時間をファイル数レコード分だけ生成
xargs -I@ date -d @ +'%Y%m%d%H%M'                                                     | # 生成したUNIX時間をYYYYMMDDhhmm形式に変換
paste - <(find /tmp/etc -type f)                                                      | # "時間 ファイル"の形式で出力
sed 's/.*/touch -t &/'                                                                | # touch コマンドの生成
sh                                                                                      # タイムスタンプの更新

方法1: statコマンドで実現

@Heliac1999さんから助言をいただいて考えた方法。
GNU coreutilsに含まれる、ファイルやファイルシステムの状態を出力するコマンド、 stat を利用する。
stat コマンドは、 -c オプションで出力フォーマットを指定できるため、それを活用する。

01 #!/bin/sh
02
03 find DIR -type f          | # ファイルを再帰的に探索・表示
04 xargs stat -c '%n %Y'     | # 各ファイルについて、"ファイル名 更新時間(UNIX時間)"の形式で出力
05 sort -k 2,2n                # 第2フィールドをキーとして、数値順にソート

ワンライナー版。

$ find /tmp/etc -type f | xargs stat -c '%n %Y' | sort -k 2,2n

方法2: findコマンドで実現

@ebanさんから助言をいただいて考えた方法。
find コマンドのアクション -printf で、ファイルのタイムスタンプを表示できる事を利用する。

01 #!/bin/sh
02
03 find DIR -type f -printf "%p %TY%Tm%Td %TH%TM%TS\n" | # ファイルを再帰的に探索し、"ファイル名 更新時間"の形式で出力
04 sort -k 2,2n -k3,3n                                   # 第2フィールド(yyyymmdd)、第3フィールド(hhmmss)の順にキーとして、数値順にソート

以下、コピペ実行用のワンライナーコード。

$ find /tmp/etc -type f -printf "%p %TY%Tm%Td %TH%TM%TS\n" | sort -k 2,2n -k3,3n

参照にしたもの

→ お二人共、いつも的確な助言をくださって、ありがとうございます!

雑記

  • 今回の実験で初めて利用したのだけれど…​ stat コマンドは、非常に多くのファイル情報を表示できる。
    -c オプションで出力書式も細かく指定できるし、アイデア次第では様々な用途に活用できるかも?

$ stat /etc/bash.bashrc /dev/stdin
  File: '/etc/bash.bashrc'
  Size: 576             Blocks: 8          IO Block: 4096   通常ファイル
Device: 802h/2050d      Inode: 1311927     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2016-11-24 21:02:06.274472644 +0900
Modify: 2016-11-15 04:06:22.000000000 +0900
Change: 2016-11-21 22:18:13.879769496 +0900
 Birth: -
  File: '/dev/stdin' -> '/proc/self/fd/0'
  Size: 15              Blocks: 0          IO Block: 4096   シンボリックリンク
Device: 6h/6d   Inode: 9222        Links: 1
Access: (0777/lrwxrwxrwx)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2016-11-24 21:04:25.153479914 +0900
Modify: 2016-11-24 21:00:38.516666575 +0900
Change: 2016-11-24 21:00:38.516666575 +0900
 Birth: -
  • 本日の懺悔。
    dateutils (http://www.fresse.org/dateutils/) に含まれる datesort を使えば、複数のファイルをタイムスタンプでソートできると、勘違いしていました…​orz
    → 間違いをお詫びすると共に、本記事にて訂正いたします m(_ _)m

で き ま せ ん ! !

Tags: シェル芸 Unix
このエントリーをはてなブックマークに追加