はじめに
私は寡聞にして存じなかったのですが、 先日 Nushell なるものを教えてもらいました
Qiita にも既にいくつも記事があるし、 2019 年からあるものなので今更感が強いですが、使ってみると何とも Elixir で、アルケミスト的にワクワクしたので記事にしておきます
(Rust 製だけど)
インストール
macOS の場合、 Homebrew でインストールできます
brew install nushell
Windows の場合は winget です
winget install nushell
ログインシェルとして設定する方法も記載されていますが、推奨はされていません
Nushell の開始
お使いのターミナル(iTerm2 や warp や Windows Terminal)から nu
を実行しましょう
nu
すると Nushell の Discord や GitHub などなどが表示されて Nushell の世界に入ります
Nushell の基本
ファイル一覧
まずファイル一覧を出してみましょう
ls
特にオプション等指定せずとも良い感じにテーブル表示してくれます
ではパイプラインを使ってみましょう
以降、画像ではなくテキストで実行結果を貼り付けます
$ ls | sort-by size | reverse
╭───┬─────────┬──────┬──────────┬───────────────╮
│ # │ name │ type │ size │ modified │
├───┼─────────┼──────┼──────────┼───────────────┤
│ 0 │ 002.txt │ file │ 3.0 MB │ 6 minutes ago │
│ 1 │ 003.txt │ file │ 608.3 KB │ 8 minutes ago │
│ 2 │ 011.txt │ file │ 1.7 KB │ 8 minutes ago │
│ 3 │ yyy │ dir │ 160 B │ 3 minutes ago │
│ 4 │ xxx │ dir │ 128 B │ 3 minutes ago │
│ 5 │ 001.txt │ file │ 16 B │ 9 minutes ago │
╰───┴─────────┴──────┴──────────┴───────────────╯
「ファイル一覧を取得して」 |
「サイズ順に並び替えて」 |
「順序を逆にする」というのが直感的にわかりますね
通常の bash だと以下のようになります
$ ls -lhS
total 7608
-rw-r--r-- 1 oec staff 2.9M 3 18 11:50 002.txt
-rw-r--r-- 1 oec staff 594K 3 18 11:48 003.txt
-rw-r--r-- 1 oec staff 1.7K 3 18 11:48 011.txt
drwxr-xr-x 5 oec staff 160B 3 18 11:53 yyy
drwxr-xr-x 4 oec staff 128B 3 18 11:53 xxx
-rw-r--r-- 1 oec staff 16B 3 18 11:47 001.txt
「短いことこそが価値である場合」には良いですが、オプションを覚えていないと全く理解できませんね
ちなみにファイルサイズが違うのは、 1,024 で割っているか 1,000 で割っているかの違いです
1,024 で割った方(bash の表現)は厳密には KiB (キビバイト)ですね
続いてフィルターです
$ ls | where size > 1kb
╭───┬─────────┬──────┬──────────┬────────────────╮
│ # │ name │ type │ size │ modified │
├───┼─────────┼──────┼──────────┼────────────────┤
│ 0 │ 002.txt │ file │ 3.0 MB │ 21 minutes ago │
│ 1 │ 003.txt │ file │ 608.3 KB │ 23 minutes ago │
│ 2 │ 011.txt │ file │ 1.7 KB │ 22 minutes ago │
╰───┴─────────┴──────┴──────────┴────────────────╯
サイズが 1 KBより大きいものを抽出する、というのが読み取れます
ちなみに bash だと以下のようになります
$ find . -maxdepth 1 -type f -size +1k | xargs ls -ldh
-rw-r--r-- 1 oec staff 2.9M 3 18 11:50 ./002.txt
-rw-r--r-- 1 oec staff 594K 3 18 11:48 ./003.txt
-rw-r--r-- 1 oec staff 1.7K 3 18 11:48 ./011.txt
最早読めというのが無茶だし、ググらずに書くのは私には不可能です
プロセス一覧
プロセスの一覧を表示したり、特に重い処理を探したりするのも楽チンです
$ ps | where cpu > 5
╭───┬───────┬────────────────────┬─────────┬───────┬──────────┬─────────╮
│ # │ pid │ name │ status │ cpu │ mem │ virtual │
├───┼───────┼────────────────────┼─────────┼───────┼──────────┼─────────┤
│ 0 │ 75346 │ nu │ Running │ 9.87 │ 13.3 MB │ 35.0 GB │
│ 1 │ 61476 │ iTerm2 │ Sleep │ 15.09 │ 125.8 MB │ 35.7 GB │
│ 2 │ 3572 │ qemu-system-x86_64 │ Sleep │ 8.69 │ 481.2 MB │ 48.2 GB │
╰───┴───────┴────────────────────┴─────────┴───────┴──────────┴─────────╯
現在時刻の取得
現在時刻も分かりやすく date now
で取得できます
$ date now
Sat, 18 Mar 2023 12:23:16 +0900 (now)
システム情報の取得
sys
で OS などの情報を取得することができます
これは実行結果が大きすぎるため実際の実行結果は貼り付けませんが、以下のような形式になっています
╭───────┬───────────────────╮
│ host │ {record 6 fields} │
│ cpu │ [table 4 rows] │
│ disks │ [table 3 rows] │
│ mem │ {record 4 fields} │
│ temp │ [table 1 row] │
│ net │ [table 4 rows] │
╰───────┴───────────────────╯
つまり、 Elixir でいうところの Map になっています
では host のところだけ取り出してみます
$ sys | get host
╭─────────────────┬───────────────────────────╮
│ name │ Darwin │
│ os_version │ 13.2.1 │
│ long_os_version │ MacOS 13.2.1 │
│ kernel_version │ 22.3.0 │
│ hostname │ xxxxx │
│ uptime │ 1wk 1day 13hr 56min 51sec │
│ boot_time │ 2023-03-09T22:38:35+09:00 │
| sessions | [table 3 rows] |
╰─────----------──┴──--------─────────────────╯
| get host
で取得できます Elixir でいうところの |> Map.get(:host)
ですね
実際には sessions の右側にテーブルでセッションの一覧が表示されます
更に OS バージョンだけを取得したい場合は以下のようになります
$ sys | get host.os_version
13.2.1
以下のように実行することで、現在システムにログインしているユーザーの一覧が取得できます
$ sys | get host.sessions.name
╭───┬──────────────╮
│ 0 │ _mbsetupuser │
│ 1 │ oec │
│ 2 │ root │
╰───┴──────────────╯
そして、ここからが Elixir like というか関数型っぽさというかです
List の各値に対してコマンドを適用することができます
$ sys | get host.sessions.name | each { |it| ^echo $it }
_mbsetupuser
oec
root
アルケミストの皆さんは、この each
が Elixir でいうところの Enum.map
であることが分かりますね
map がなくて each な点に注意
each {|各値を表す変数名| 実行したいコマンド}
で List の各値にコマンドを適用します
上の例で ^echo
のように ^
(ハット)が付いているのは、 Nushell の echo
ではなく、通常の echo
を使わせる為です( ^
がないとテーブル表示になる)
当然、 each があれば reduce もあるわけで、つまりこれを使えば関数型っぽく何でも処理できるわけです
reduce
例えば .txt
ファイルの合計サイズは以下のようにして算出できます(もっと別の方法もあると思いますが)
$ ls | where name =~ ".txt" | reduce -f 0b {|it, acc| $acc + $it.size}
3.7 MB
each {|各値を表す変数名, 前の実行結果を表す変数名| 実行したいコマンド}
で List の各値毎に再帰的にコマンドを適用していき、最終結果を取得します
-f
で acc (前の実行結果)の初期値を 0 バイト(ファイルサイズ型)に指定しています
スクリプト
もちろん、スクリプトとして実行することもできます
以下のような内容のファイルを text_sum.nu という名前で保存しましょう
#!/usr/bin/env nu
ls
| where name =~ ".txt"
| reduce -f 0b {|it, acc| $acc + $it.size}
| format filesize MB
実行権限を付けておきます
chmod +x text_sum.nu
これで .txt
を含むファイルの合計サイズを MB 単位で出力してくれます
以下のようにして実行できます
$ nu test_sum.nu
3.7 MB
また、先頭にシバン(#!/usr/bin/env nu
)をつけたので、単に ./test_sum.nu
で実行することも可能です
特にパイプで改行するとき \
によるエスケープが不要なのが良いですね
最早 Elixir と全く同じ感覚で書けます
まとめ
Nushell は手早くファイルを探索したり操作したりするとき、結構使えそうです
また、Elixir のような書き方は可読性が高く、データ処理の記述に向いていることが再認識できました