シェルスクリプト用のファイル名・拡張子取得･パス正規化ライブラリ

## はじめに

シェルスクリプト用のファイル名・拡張子取得･パス正規化ライブラリ「sh-path」を作りました。シェルスクリプト用のコマンドにディレクトリ名を取得する `dirname` コマンドとファイル名を取得する `basename` コマンドがありますが実際の用途ではこれだけでは足りません。拡張子を取り除いたり取得する方法が（簡単では）なかったり、相対パスが含まれているパスを正規化したり、相対パスと絶対パスを変換するコマンドが欠けています。

また `dirname` コマンドや `basename` コマンドは機能不足で、外部コマンドなので遅い、オプションと見なされないように `--` が必要、コマンド置換を使うから末尾の改行が消えてしまうなどの細かい問題があります。このライブラリはそういった細かい問題を解決しています。

- プロジェクト: https://github.com/ko1nksm/sh-path
- 関連： [POSIX準拠でもreadlink -f （完全互換版）](https://qiita.com/ko1nksm/items/873cfb9c6ceb6ef32ec9)

## 関数一覧

基本的には他の言語のライブラリを参考に開発しており、どこかで見たような名前と機能のシェル関数なのでそんなに迷うことはないでしょう。

| 関数名           | 機能                                             |
| ---------------- | ----------------------------------------------- |   
| `path_dirname `  | ディレクトリ名の取得（`dirname` コマンド相当）     |
| `path_basename`  | ファイル名の取得（`basename` コマンド相当）        |
| `path_extname`   | 拡張子の取得                                     |
| `path_rootname`  | 拡張子を取り除く（ディレクトリとファイル名のみ残す） |
| `path_normalize` | パスの正規化を行い絶対パスに変換する                |
| `path_relative`  | 絶対パスを相対パスに変換する                       |

使い方は `dirname` コマンドや `basename` コマンドと違い、第一引数に指定した変数名に戻り値を返します。これはコマンド置換をさけることでパフォーマンスの改善と末尾の改行が消えなくするための工夫です。

```sh
# 一般的なコマンドの使い方
dir=$(dirname -- /home/koichi/.bashrc)
echo "$dir" # => /home/koichi
base=$(basename -- /home/koichi/.bashrc)
echo "$base" # => .bashrc

# sh-pathの使い方
path_dirname dir /home/koichi/.bashrc
echo "$dir" # => /home/koichi
path_basename base /home/koichi/.bashrc
echo "$base" # => .bashrc
```

拡張子の取得や拡張子の削除を行うときに、サフィックスを指定することができます。使い方はおそらく例を見たほうが早いでしょう。特徴として `.tar.gz` のような多重の拡張子にも対応しています。

```sh
path_extname ret "/var/tmp/file.tar.gz" .tar
echo "$ret" # => なし

path_extname ret "/var/tmp/file.tar.gz" .gz
echo "$ret" # => .gz

path_extname ret "/var/tmp/file.tar.gz" .tar.gz
echo "$ret" # => .tar.gz

path_extname ret "/var/tmp/file.tar.gz" ".*.*"
echo "$ret" # => .tar.gz

path_extname ret "/var/tmp/file.tar.gz" ".**"
echo "$ret" # => .tar.gz
```

ソースコードは例（？）のグローバル変数を使わずに POSIX シェルの機能だけで実現するために位置パラメータを多用しているので読みづらいです。だからこそ短く書いているので読めないと言うほどでもないと思います。

## 制限

現在 [BusyBox for Windows (busybox-w32)](https://frippery.org/busybox/) には対応していません。busybox-w32 のパスは Windows のパスと同じで `C:\Windows` や `'\\server\koichi'` のようなパスが使えて対応が面倒なんですよ・・・。そのうち気が向いたら対応しますがコードは完全に分離しそうな気がしています。

## 関数

関数ごとの使い方と例です。

### path_dirname

```txt
Usage: path_dirname varname pathname
    varname: variable name
    pathname: path name
```

```sh
path_dirname ret "/var/tmp/file.txt"
echo "$ret" # => /var/tmp
```

### path_basename

```txt
Usage: path_basename varname pathname [suffix]
    varname: variable name
    pathname: path name
    suffix: .*, .*.*, .**, .ext (default: NULL)
```

```sh
path_basename ret "/var/tmp/file.txt"
echo "$ret" # => file.txt
```

### path_extname

```txt
Usage: path_extname varname pathname [suffix]
    varname: variable name
    pathname: path name
    suffix: .*, .*.*, .**, .ext (default: .*)
```

```sh
path_extname ret "/var/tmp/file.txt"
echo "$ret" # => .txt
```

### path_rootname

```txt
Usage: path_rootname varname pathname [suffix]
    varname: variable name
    pathname: path name
    suffix: .*, .*.*, .**, .ext (default: .*)
```

```sh
path_rootname ret "/var/tmp/file.txt"
echo "$ret" # => /var/tmp/file
```

### path_normalize

```txt
Usage: path_normalize varname pathname [basedir]
    varname: variable name
    pathname: path name
    basedir: base directory (default: PWD)
```

```sh
path_normalize ret "/var///tmp/.././file.txt"
echo "$ret" # => /var/file.txt
```

### path_relative

```txt
Usage: path_relative varname pathname [basedir]
    varname: variable name
    pathname: path name
    basedir: base directory (default: PWD)
```

```sh
path_relative ret "/var/tmp/file.txt" /var/
echo "$ret" # => ./tmp/file.txt
```

## さいごに

かなり昔に bash 専用で書いたコードを掘り起こして、拡張子周りの機能を追加して、POSIX シェルの機能だけで動くようにして、最低限のテストを付け加えただけなので、テストが甘いかもしれません。将来的に総合的なシェルスクリプト用の標準ライブラリを作る予定なので、それまでのつなぎとして機能の一部の先行リリース扱いです。