何故シェルスクリプトを勉強するのか

2024/06/20

最近、リアルやTwitter問わずシェルスクリプトを((中には)基本的な文法さえ理解しようとせずに)嫌う人が非常に多いように思います。

POSIXに則ったシェルの話をすれば、配列も変数スコープも無いスクリプト言語を今更勉強したがる人がいないのは自明であるかもしれません。

しかしBash 5.xやZshになれば、配列どころか連想配列もあるしローカル変数もあります。何なら最新のPOSIXでさえもIFSの仕様が定義されました。

というわけで、全てのプログラマがシェルスクリプトを勉強するべき理由を紹介します。

前置き

この記事には多分なポジショントークが含まれています。

インターネット上の記事の内容は鵜呑みにせず、自分の中で真偽を見極めることが重要です。

この記事の対象者

  • Linuxは使うつもり無いから…とシェルスクリプトから逃げている人
  • 面倒だから…とシェルスクリプトから逃げている人
  • 今どきシェルスクリプトなんて流行らないから…とシェルスクリプトから逃げている人
  • その他適当な理由によりシェルスクリプトから逃げている人

対象ではない人

  • 軽めのワンライナーを書ける
  • 自分はGUI以外使わず、絶対に自動化しないぞという強い決心がある

理由1. 高度な文字列操作

シェルスクリプトはgrepsedawkにより非常に高度な文字列操作を提供します。

簡単な文字置き換えや検索は1行で行えます。

# hoge.txt内でappleという文字が入った行を表示する
grep "apple" "hoge.txt"
 
# hoge.txtでappleを全てbananaに置き換えた結果を出力する
sed "s/apple/banana/g" "hoge.txt"
 
# hoge.txtで行頭に#がある行の番号を表示する
grep -n  "^#" "hoge.txt" | cut -d ":" -f 1
 
# hoge.txtでappleが含まれる行の数をカウントする
grep 'apple' "hoge.txt" | wc -l

テキストファイルの加工、抽出は非常に得意分野です。

Goならos.WalkDirで数十行必要なファイルの再起検索もお手の物。

find "."

乱数を20個出力して番号順にソート

seq 20 | xargs -L 1 'bash' '-c' 'echo $RANDOM' | sort -n

他の言語ならどれも数行を要する内容ばかりです。

以下のスクリプトはMarkdownで書かれたニコニコ動画のリンクリストを番号順でソートするスクリプトです。(Bash 5をゴリゴリ使っているのはお許しください)

同様のことをPythonやGoで書こうとするとそれなりの行数を必要とする処理ですが、シェルスクリプトだと10行未満で記述できます。

(私は正規表現やawkに疎いので、それらに詳しい方ならもっと短く正確に書けると思います)

linkfile="links.md"
while read -r url; do
    sed -E "s|- \[(.+)\]\((.+)\)|\1\n\2|g" "$linkfile" | grep -B 1 "$url" | head -n 1 | sed -e "s|^|- [|g" -e "s|$|]($url)|g"
done < <(
grep -- "- " "$linkfile" |  sed -E "s|- \[(.+)\]\((.+)\)|\2|g" | tr "/" " " | sed "s| sm| sm |g" | sort -n -k 5 | sed "s| sm | sm|g" | tr " " "/"
) >> "index.md"

ファイルの加工、検索、変更という処理においてシェルスクリプトは非常に強力な言語です。

全力で頑張ればAPIを利用したVPNサーバの自動取得、GUIによる操作ウィンドウ、自動接続等を実装することもできます。

Hayao0819/nm-vpngate

流石に最後のこれはシェルスクリプトで記述するべき内容かと言われれるとかなり微妙ですが、可能性を感じていただけると思います。

シェルスクリプトは一度動くものを書いてしまえばめったに壊れることはありません。バージョン等を機にせずともポータビリティの非常に高いコードを記述できます。

理由2. 人類がOSと対話する方法は2種類しかない

人類がOSと対話するには、究極的にはCLIかGUIを経由するしか方法はありません。そしてUnix/Linux系OSで標準で備わっている手段は前者のみです。

シェルスクリプトはともかく、シェルを介さないと人類はOS上でHello Worldすら実行できません。

すなわち、全ての道はシェルから行われているのです。GUIもCLIもシェルの一種ですから、これは間違いではありません。

そして、LinuxにおけるGUIによる自動化はWindowsと比較して貧弱です。なぜならCLIによる伝統的な自動化が非常に強力だからです。

CLIによる自動化、それこそまさにシェルスクリプトです。シェルによって他の言語で記述された実行可能ファイルを起動することにより、コンピュータを自由に操ることができます。

GUIによるOSの操作には自由度の限界があり、CLIに柔軟性で勝ることはありません。すなわち、真にOSを操作する方法こそCLI、シェルスクリプトなのです。

(Windowsはこの限りではりません。Windows XP以前のシェルはバッチスクリプトのみであり、非常に自由度と柔軟性が低いものでした。

しかしMS-DOSの時代はまさにこれとBASICでコンピュータを操作したというのですから非常に驚きです。

PowerShellが登場したモダンな環境においても、PowerShellスクリプトは個人が簡単に実行、配布できるものではなさそうです。

というのも、署名のされていないPowerShellスクリプトの実行は少々面倒です。

他にもVBScriptやJScript等それなりに便利なものもあったんですが、セキュリティの関係上非推奨となり今後削除されるそうです。

)

理由3. あらゆる自動化に用いられる

シェルスクリプトによる自動化はありとあらゆる場所で利用されています。

伝統的なものでは、Makefileによる自動化やaliasによる省略などが当てはまるでしょう。

Dockerfileによるコンテナイメージの構築や、.github/workflowによるCI、package.jsonによるタスクランナーもこれに該当します。

言い換えれば様々なモダンなプロジェクトで当然のように利用されているこれらは全てシェルスクリプト(を主体として)記述していきます。

一生これらをメンテナンス+利用しないぜ!という相当の曲者以外はシェルスクリプトに間接的に触れていることになります。

そしてエンジニアとして重要なのがセキュリティです。他人が書いたpackage.jsonやMakefileを中身を全く見ずに実行するのはセキュリティ的に好ましくないのは自明でしょう。

つまり、たとえ自分がシェルスクリプトを書くことはなくても処理を理解する必要があるのです。

理由4. Windows以外のOSに依存せず、ランタイムもなしに実行できる唯一のスクリプト言語である

OSやアーキテクチャに依存せず、ランタイムもコンパイル環境もなしに実行できる言語はシェルスクリプトかUEFI実行ファイルしか存在しないと思っています。

CやGoが吐くようなバイナリはOSやアーキテクチャに依存するのは当然ですし、PythonやRubyのようなスクリプト言語もその言語本体をインストールする必要があります。

最小限の追加コンポーネントでコンパイルやコンバートを行わずにLinux, macOS, BSDで実行できる言語はシェルスクリプトしかないでしょう。

Windowsでさえも、WSLなどという巨大なものを使わなくてもbusybox.exeやMSYS2で対応できます。

すなわち、シェルスクリプトはそのファイル単体において動作する環境が圧倒的に多い言語なのです。

もちろんインストールされているコマンドやシェルのバージョンに依存しますから、現実的にはそこまで多いことはないでしょう。

しかし、POSIXに準拠したシェルスクリプトを書けば殆どどこでも動作するはずです。

言い換えれば、この特性は様々な言語のブートストラップとして利用することができます。実行されているOSを判断し、必要な依存関係をインストールするエントリーポイントとして最適な言語なのです。

理由5. 非常に高い後方互換性

シェルスクリプトの文法はすでにかなり昔から存在し、成熟しています。よって今後その文法に破壊的変更が加えられることはありませんし、今動いているものは(同じシェルで実行すれば)どこでも動作します。

最近のスクリプト言語は、同じ実行ランタイムでもやれバージョンの違いだのライブラリのバージョンの違いだのですぐに動作しなくなります。

成熟している言語だからこその安定性と軽量さ、ポータビリティを享受できる素晴らしい言語です。

理由6. 知識を様々な言語、技術で活用できる

標準出力と標準エラー出力の切り替えをしっかりと行い、この仕組みを活用している言語は結構少ないように感じます。

これら標準入出力や環境変数、更にはEverything is a fileの哲学など、シェルスクリプトを通して学べるOSの機能は非常に多岐にわたります。

これらの仕組みを知ることで、シェルスクリプト以外の部分でもこれを活用することができます。

# 標準エラー出力にHello World
echo "Hello World!" >%2
// 標準エラー出力にHello World
fmt.Fprintln(os.Stderr, "Hello World!")

理由7. サーバ向けLinuxを操作できるのはシェルしか無い

ヘッドレスな環境用のOSにGUIなんていうデカいものはありません。当然操作はシェルのみです。

lsとcdとnanoで全ての操作を完結することは不可能ですし、非常に非効率的です。

Linuxサーバの構築と管理にはシェルスクリプト(少なくとも基本的なシェルコマンド)が必要不可欠なことは言うまでもありません。

一昔前のオンプレミスサーバーならば、管理用のシェルスクリプトを書くようなこともあるでしょう。

終わり

シェルスクリプトは現代においても勉強する価値のある言語・技術の一種でしょう。

むしろクラウドで様々な操作がWebUIになり、「今すぐシェルスクリプトがないと何も操作できない」というシチュエーションが減少した現代でこそ、どこかで決心をしてシェルスクリプトに真剣に向き合わないといざというときに痛い目に遭います。

黒い画面でワンライナーをカタカタ書いてすぱーんと実行できればカッコいいのは間違いありません。今こそシェルスクリプトを勉強するときです。