車輪の再発明：Bashライブラリ内の関数定義をShellScriptにマージするlibMerge

# 前書き
本記事では、自作したPython3.xスクリプトのlibMergeについて記載します。
libMergeは、Bashライブラリ内の関数定義をShellScriptにマージする機能を持ちます。
正確には、Script内で使用されている関数の定義部分のみをBashライブラリから抽出し、
「抽出した関数＋元々のScriptの内容」を記載したScriptを新規作成します。

なお、libMergeの作成理由は、以下の通りです。
　1. Bashは汎用的なライブラリが存在せず、ライブラリ内の関数の機能に関する共通認識がない。
　　 そのため、第三者が関数定義を参照しやすいScriptの提供が好ましい。
　2. 第三者のために、ライブラリの関数定義をScriptに手動で転記する事は避けたい。
　3. 単一のファイルで完結していないScriptは、可搬性が低い。
　　 ライブラリがインクルードできない場合、ライブラリが改変された場合などは動作しなくなる。

ちなみに、2005年に[「シェルスクリプトを共有してマージするshsubrmerge(CodeZine)」](https://codezine.jp/article/detail/68)という記事で、
ほぼ同じ機能のScriptが公開されています。まさに、車輪の再発明です。
さらに言及すれば、同等の機能を持つ有名なパッケージが存在しないようなので、
需要がない事が予想されます。~~自分の問題を解決できるから良いんだ(・言・)~~

# 目次
　1. libMergeのインストール
　2. 参考ライブラリ：bashLib
　3. libMergeのオプション
　4. libMergeの使用手順
　5. libMergeの処理(仕組み)
　6. Limitation 

# libMergeのインストール
[Github](https://github.com/nao1215/OriginalCommands)に公開してあるため、以下の手順(Debian想定)で取得できます。
libMergeのインストールディレクトリは、"/usr/local/bin/"以下となります。

```
$ git clone https://github.com/nao1215/BashScriptsCompilation.git -b master
$ cd BashScriptsCompilation
$ sudo ./installScripts         # インストールスクリプト
```

# サンプルライブラリ：bashLibについて
Bashライブラリをお持ちの方は少ないと思われるので、サンプルライブラリbashLibを用意しました。
先ほどのインストール手順によって、bashLibもインストールされます。

なお、bashLibに存在する関数は、以下の通りです。


| 関数名            | 説明                                            |  
|:------------------|:------------------------------------------------|  
| errMsg            | 標準出力にエラーメッセージを赤文字で表示        |  
| warnMsg           | 標準出力にエラーメッセージを黃文字で表示        |  
| checkArgc         | 引数の個数をチェック                            |  
| isFile            | ファイルが存在するかどうかをチェック            |  
| isDir             | ディレクトリが存在するかどうかをチェック        |  
| isSubshell        | scriptが"source"コマンドで実行されているかを確認|  
| isNumeric         | 変数が数値化どうかをチェック                    |  
| isNull            | 変数(文字列)がNullかどうかをチェック            |  
| isRoot            | ユーザがrootかどうかをチェック                  |  
| ++                | 変数(数値)をインクリメント                      |  
| --                | 変数(数値)をデクリメント                        |  
| AplusB            | 変数に指定の値を加算                            |  
| AminusB           | 変数に指定の値を減算                            |  
| \_sum             | 変数(数値)を加算                                |  
| makeDir           | 各種確認の後、ディレクトリを作成                |  
| makeEmptyFile     | 各種確認の後、0Byteのファイルを作成             |  
| getLineNum        | ファイルの行数を取得                            |  
| dos2unix          | ファイルをwindows形式からunix形式に変換         |  

  

# libMergeのオプション

| オプション   | 説明                                               |
|:-----------:|:---------------------------------------------------|
| -l(--lib)   | BashライブラリへのPATHを記載。|
| -s(--script)| ライブラリ内の関数定義を付与したいScriptへの<br>PATHを記載。  |
| -o(--output)| 関数定義を転記したスクリプト(新規作成)の名前を<br>変更したい場合、ファイル名を記載。<br>(デフォルト："m_\<original_scriptname\>)" |


# libMergeの使用手順
例として、以下のようなサンプルスクリプト(sample.sh)を用意します。
sample.shでは、"/usr/local/bin/bashLib"内で定義されたmakeDir()、errMsg()を使用しています。
これらの関数を使用するため、"source"コマンドによってライブラリをインクルードする必要があります。

```sample.sh
#!/bin/bash
# Description : Sample Script

source /usr/local/bin/bashLib  # ライブラリのインクルード
makeDir "dir"                  # ライブラリ関数
errMsg "Sample"             # 同上
```
```実行結果.
$ ./sample.sh 
make directory dir
Sample
```

libMergeを用いて、前述のsample.shに、関数定義を付与します。
なお、sample.shに直接関数の定義を挿入するのではなく、m_sample.shという新規Scriptが生成されます。
ここでの新規Scriptの名前は、"-o"オプションで変更する事ができます。

```
$ ls
sample.sh
$ libMerge -s sample.sh -l /usr/local/bin/bashLib
$ ls
m_sample.sh  sample.sh
```

生成されたm_sample.shは、以下の通りです。
関数定義はScriptのヘッダ以下に挿入され、Script(sample.sh)で用いられている関数定義しか転記されません。

```m_sample.sh
#!/bin/bash                                                                           
# Description : Sample Script
function errMsg() {
    local message="$1"
    echo -n -e "\033[31m\c"  # Escape sequence to make text color red 
    echo "${message}"
    echo -n -e "\033[m\c"  　# Escape sequence to restore font color
}

function makeDir() {
    local dir_path="$1"

    checkArgc "$#"
    if [ -d "${dir_path}" ]; then
        echo "Directory "${dir_path}" already exists. Not make it."
    else
        mkdir -p $1
        if [ "$?" = 0 ]; then
            echo "make directory "${dir_path}""
        fi    
    fi  
}


source /usr/local/bin/bashLib  # ライブラリのインクルード

makeDir "dir"
errMsg "Sample"
```

# libMergeの処理(仕組み)
以下に、libMergeの処理の概要を示します。

　1. オプションチェック(不備があればエラー通知)
　2. Bashライブラリをパースし、関数定義の開始行番号と終了行番号のリストを作成
　3. Bashライブラリに存在する関数の名前を取得
　4. 手順2の結果を元に、関数単位で関数の定義部分を取得。関数名をキー値として、定義を辞書で管理
　5. Scriptをパースし、Script内で使用している関数以外をリスト、辞書から削除
　6. Scriptのコピー(m_\<script_name\>)の作成中、辞書の要素(関数定義)をScriptのヘッダ以下に挿入

# Limitation 
　・関数定義部分の抽出(特に定義終了部)は、改良の余地があります。
　・元のScriptに存在するライブラリインクルード処理を削除する機能は、未実装です。
　・十二分なテストを実施していません。

# 最後に
「2017年の目標に"パッケージ作成(C言語)"があるから、libMergeをC言語で実装しよう」
そのような事を設計時(20117/2/17の睡眠時)に考えましたが、Pythonで実装して正解でした。

また、Bashのライブラリ化を難しくしている原因は、
「戻り値の欠如」「リダイレクト・パイプを駆使するBashの性質」ではないかと、
今回のlibMergeおよびbashLibの作成で身を持って体感しました。
ライブラリ化の利点は、「ワンライナー(シェル芸)の可読性向上」「テンプレ処理の集約」ぐらいで、
それ以上を求めた優れたライブラリの作成を試みる場合、泥沼に陥りそうです。