ファイルを再帰的に探索し更新時間でソートするワンライナー
本日、ぱぴろん(@papiron)さんの次のツイートを拝見して、 ls
コマンドを用いる以外の方法はあるのだろうかと、少し疑問に感じた。
またこの記事が役に立っているのだが、役に立つ状況がトラブル対応なのがカナシイ。ファイルを再帰的に更新時刻で並べ替えるワンライナー
— ぱぴろん (@papiron) November 24, 2016
https://t.co/YwSApV04is #シェル芸
そこで、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
参照にしたもの
ファイルを再帰的に更新時刻で並べ替えるワンライナー - 日々之迷歩
→ 今回の記事のネタ元。福岡の著名シェル芸人、ぱぴろんさんの記事。ベテランシェル芸人、 @ebanさんと、 @Heliac1999さんのアドバイス。
@gin_135 findに-lsオプションがあります。また-printfオプションを使えば好きな形式にできます
— eban (@eban) November 24, 2016
@gin_135 stat -c '%Y' にするとか(epoch timeからの秒数)
— Heliac1999 (@Heliac1999) November 24, 2016
→ お二人共、いつも的確な助言をくださって、ありがとうございます!
雑記
今回の実験で初めて利用したのだけれど…
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
@papiron lsを使わない方法は無いのか少し疑問に思って、調べてみました。dateutilsのdatesortを使うと、簡単に出来るみたいですよ(^O^)/
— ginjiro (@gin_135) November 24, 2016
$ find DIR -type f | datesort #シェル芸
→ で き ま せ ん ! !