初歩のシェルスクリプトで遊ぶ[シェルスクリプトの注意点はどこだろう(4)]

繰り返し使うスクリプト部品はどうやって分離する?

このあたり試行錯誤中です。来月には取り下げてるかも。

使える手段は何?

ドットコマンドで、というのは、シェル関数やシェル変数を書いたテキストを読み込むのではなく、実行したいコマンドをそのまま書いて、読み込むことです。

ごく小規模で簡単なスクリプトでの、ディレクトリとファイルの分け方案

最近、HTMLファイルを出力するスクリプトを書きました。大量のsvg画像ファイルを、WEBブラウザで一覧表示します。
これのスクリプトディレクトリを例にします。いちおうダウンロードのところに置きましたが、読む必要はありません。

前提
  • ファイルは分離する。
  • readlinkコマンドを使うことを妥協する。
  • ごく小規模に限定する。中規模以上は対象外。
  • 理解しやすい仕組みにする。

ユーザがシェルから実行するのは、上の「svg2html.sh」です。
「Path」ディレクトリの中には、画像の下半分のように、シェルスクリプトとシェル関数ファイルが置いてあります。

  1. 処理の中心になるスクリプトファイルを、ディレクトリに置く。実行するときにシェルから直接に叩くファイル。
  2. スクリプトと並べて、「Path」ディレクトリを作る。
  3. 「Path」ディレクトリの中に、単機能で作ったスクリプトファイルを置く。
  4. シェル関数を書いたファイルも、「Path」のなかに置く。ドットコマンド「.」で読み込んで使う。

ごく小規模に限定、というのは、ええと……
beta-reverse.hatenadiary.jp
初歩のシェルスクリプトで遊ぶ[端末の256色カラーセレクタ(4)] - HatenaDiary id:Narr
このくらいなら問題なくいけるんじゃないでしょうか。
これより大きくするなら、もっと別のディレクトリの分け方組み方を考えたほうがいいと思います。

スクリプト冒頭の初期設定

#!/bin/sh

# シェルスクリプトファイル本体のディレクトリを取得
# PATHに外部ファイル保存用ディレクトリ「Path」を追記する
scriptDir="$(cd -- "$( dirname -- "`readlink -f -- "${0}"`" )" && pwd)"
PATH="${scriptDir}/Path:${PATH}"

# シェル関数ファイルの読み込みと確認
if . svg2html.func
then true
else exit 2
fi

「Path」を環境変数PATHに追加します。
「Path」の中のシェルスクリプトファイルと、ドットコマンドで読み込んだシェル関数を、名前を叩くだけでコマンドのように実行できます。
readlinkはリンクの関係で必要です。

使い方のコツのようなもの

ルートのスクリプトファイルは肥大化を避ける

言うまでもなく、部品はなるべく単体のスクリプトファイルやシェル関数に分けて、「Path」に入れます。

シェル関数に慣れないうちは、補助輪をつけて使う
htmlStart(){ ( # html開始からdlタグの直前まで
# ${1} <title>

echo '<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">'
echo '<title>'"${1}"'</title>'
echo '<link rel="stylesheet" href="./css_svg.css" >
</head>
<body>'

) }

定型文字を吐き出すだけのシェル関数です。
特別なシェル関数ではありません。普通のサブシェルを入れただけです。
重要なのは、サブシェルを必要がなくても入れることです。
このサブシェルの括弧は、「自転車の補助輪」のような働きをします。性能を制限する代わりに、シェル関数でよくやらかす失敗を防げる。

  • 呼び出し側のシェル変数に値を入れる使い方はしない。
  • 標準入出力でパイプで繋いで使う。一度だけ起動して多めのデータを流す。
  • 数百回も呼び出したりはしない。
  • 呼び出し側のシェル変数が増えたり、書き換えられたり、カレントディレクトリが変化したり、IFSが書き換えられるのは困る。ミスが怖い。
  • 呼び出し側のシェル変数を、書き換えはしないが読みたい。引数でいちいち渡すのは面倒。
  • 個別のスクリプトファイルに分けてもいいが、そこまでするほどの規模ではない。

このようなとき、シェル関数を中で丸ごとサブシェルにします。「()」で括ってしまう。
スクリプトファイル単体にするよりは使い勝手が悪いですが、単体のスクリプトファイルに近い使い方ができます。標準入出力や引数も使えます。

多少の注意点に合格しているなら、シェルスクリプトファイルに書いたスクリプトを、そのままこのシェル関数に入れることができます。
逆に、想定以上に規模が大きくなってしまったときは、単体のシェルスクリプトファイルに書き出すこともできます。

無駄はあります。たとえばコマンド置換でしか呼び出さないシェル関数なら、この補助輪のサブシェルは要りません。
しかし特性が使い方に依存しない、シェル関数の1行目を見るだけでそれが分かる、ということは、非常に楽です。
ひとまずシェル関数は補助輪サブシェルで試作し、測定後、必要なシェル関数にのみ個別に改善を検討する、というやり方もできます。

動かすシェルを切り替えられる

単体のシェルスクリプトファイルは、ファイルごとに、dashでもbashでも好みのシェルを指定できます。
呼び出し側はbashにして、分離したスクリプトはdashで高速動作させる、こともできる。

テストはどうする?

「Path」の中のスクリプトファイルを実行すると、単体で、シェバングで指定したシェルで動きます。そのままテストができます。
テスト専用のスクリプトをシェバングを含めて書けば、シェル関数もテストできます。

オプション解析を避け、ファイル自体を分ける(スクリプトファイルでもシェル関数でも)

「Path」の中のファイルが多少増えても、あまり問題がありません。
処理をスクリプトファイルの中で切り替えるよりは、スクリプトファイル自体を分けるべきです。
シェル関数にするときも同様で、オプション解析自体をしなくて済むように、関数を分けることを目指します。