シェルスクリプトでデータを文字列の長い行順にソートする方法

Bash, Linux

はじめに

データを文字列の長い行順(あるいは短い行順)に並び替えする方法について説明します。

もともとのきっかけは、findコマンドで検出したディレクトリを階層の深い順に削除しないとエラーメッセージが表示される問題を解消したいということでした。
「階層が深いディレクトリ=パスの文字列が長いディレクトリ」をどうやってソートしようかなと考えて当エントリをまとめることにしました。

検証環境

$ uname -moi
x86_64 MacBookPro11,4 Darwin

$ bash -version | head -n 1
GNU bash, バージョン 5.0.11(1)-release (x86_64-apple-darwin18.6.0)

準備

適当にディレクトリを作成して試してみます。

スペースを含んでも正しく動作するかを確認したかったので、ここではあえてスペースを含むディレクトリも作ってみました。

# 適当なディレクトリを作成する
$ mkdir -p aaa/bbb/ccc 'aaa/d ddd/ee'

# treeコマンドでディレクトリ構成を確認してみる
$ tree
.
└── aaa
    ├── bbb
    │   └── ccc
    └── d ddd
        └── ee

5 directories, 0 files

単純にfindで出力してみる

まずは単純にfindでディレクトリのみを出力してみます。

$ find . -type d
.
./aaa
./aaa/bbb
./aaa/bbb/ccc
./aaa/d ddd
./aaa/d ddd/ee

ディレクトリ構造を単純に探索していった結果に見えます。

findの実行結果を文字列の長い順にソートしてみる

今度は文字列長でソートしてみます。
やり方としては単純です。

  1. 各行の文字列の先頭にスペース区切りで 文字列長 を付与
  2. 数値でソート
  3. 付与した 文字列長 を除去
# (1)を実装
$ find . -type d \
  | awk '{ print length, $0 }'
1 .
5 ./aaa
9 ./aaa/bbb
13 ./aaa/bbb/ccc
11 ./aaa/d ddd
14 ./aaa/d ddd/ee

# (1)~(2)を実装
$ find . -type d \
  | awk '{ print length, $0 }' \
  | sort -nr
14 ./aaa/d ddd/ee
13 ./aaa/bbb/ccc
11 ./aaa/d ddd
9 ./aaa/bbb
5 ./aaa
1 .

# (1)~(3)を実装
$ find . -type d \
  | awk '{ print length, $0 }' \
  | sort -nr \
  | cut -d" " -f2-
./aaa/d ddd/ee
./aaa/bbb/ccc
./aaa/d ddd
./aaa/bbb
./aaa
.

ひとこと

これを組み合わせて、tmp_というプレフィックスを持つディレクトリのみを削除する場合は以下のようになります。

$ find . -type d -name 'name_*' \
  | awk '{ print length, $0 }' \
  | sort -nr \
  | cut -d" " -f2- \
  | while read -r d; do rm -r "$d"; done

Bash, Linux