[PowerShell] Unixシェルスクリプトとの比較 その1

2020年7月6日月曜日

bash PowerShell

シェルに慣れ親しんでいるUnix/LinuxユーザがPowerShellを使おうとしたときに、ちょっととっつきにくいところがある。
それは、Unixではgrepやsedといった個別のコマンドが担っている概念をPowerShellでは言語が担っているというところじゃないだろうか。
この考え方を受け入れると、PowerShellが昔ながらのやり方を受け継ぎつつも、それをさらに洗練しているように見えてくる。

Unixシェルでよく使われるコマンドとPowerShellがどう対応しているのか見てみよう。

/dev/null

コマンドと言っておいて、いきなりファイル。
bash$ echo foo
foo
bash$ echo foo >/dev/null
PS > echo foo
foo
PS > echo foo | out-null

out-nullはファイルではなく、コマンドレットなのでリダイレクトではなく、パイプを使う。

printf

bash$ printf "%05d\n" 123
00123
PS > "{0:D5}" -f 123
00123

繰り返し
bash$ printf "%3s\n" | sed 's/\s/abc/g'
abcabcabc
PS > "abc" * 3

繰り返しはPowerShellのほうが直感的で簡単。

seq

bash$ seq 3
1
2
3
PS > 1..3
1
2
3

1..3のようにそのまま書くだけ。

head

bash$ seq 10 | head -n3
1
2
3
PS > 1..10 | select-object -first 3
1
2
3

tail

bash$ seq 10 | tail -n3
8
9
10
PS > 1..10 | select-object -last 3
8
9
10

grep

条件に一致する要素のみの抽出。
bash$ printf "apple\nbanana\norange\n"
apple
banana
orange
bash$ printf "apple\nbanana\norange\n" | grep an
banana
orange

PowerShellでは改行とかの特殊文字を \ ではなく、`(バッククォート)を使って表現する。
PS > "apple`nbanana`norange`n"
apple
banana
orange

PS > "apple`nbanana`norange`n" | ? { $_ -match "an" }
apple
banana
orange

でも、これはうまくいかない。PowerShellのパイプは行指向ではなく、オブジェクトとしての配列を処理するからだ。
カンマ区切りでリストを作成してやってみよう。
PS > "apple","banana","orange" | ? { $_ -match "an" }
banana
orange
PS > "apple","banana","orange"
apple
banana
orange

テキストがファイルに保存されている場合はcatが行を要素にした配列に変換してくれる。
PS > cat .\fruits.txt
apple
banana
orange
PS > cat .\fruits.txt | ? { $_ -match "an" }
banana
orange

? はWhere-Objectというコマンドレットのエイリアスなので、以下でも同じ。
PS > "apple","banana","orange" | where-object { $_ -match "an" }
banana
orange

ところで、-match演算子は左辺に配列を取ることができる。スクリプト中でデータがパイプ経由ではなく、配列で存在する場合はこちらの書き方もよさそう。
PS > "apple","banana","orange" -match "an"
banana
orange