最強uconvコマンドで全角⇔半角, ひらがな⇔カタカナ, 大文字⇔小文字の変換をシェルスクリプトで行う

## はじめに

`uconv` は ICU が開発した文字変換のコマンドです。文字コードを変換したり、全角を半角に変換したり、大文字を小文字に変換したり、ひらがなをカタカナに変換したりする場合、`tr` コマンドや `nkf` コマンドを使って変換するという例を多く見かけます。しかし `tr` コマンドは Unicode に対応していなかったり POSIX ロケール以外の対応に問題があったりします。また `nkf` コマンドは Network Kanji Filter の略であることからもわかるように基本的に日本語にしか対応していません。そういった時に使えるのが `uconv` コマンドです。`uconv` コマンドはあらゆる国の文字種や文字コード変換に対応しており、Unicode 正規化や BOM の追加や削除にも対応しています。

`tr` コマンドは標準でインストールされているというメリットがありますが、日本語に対応する必要があるシェルスクリプトで、コマンドのインストールが可能な環境であれば `tr` コマンドや `nkf` コマンドの代わりに `uconv` を使うと良いでしょう。文字コード変換の `iconv` コマンドで有名な ICU プロジェクトのコマンドなので多くの POSIX に準拠している環境に移植されており、一般的なパッケージ管理システムからインストールできるはずです。

**`tr` コマンドの問題点**

```sh
# macOS (BSD版) （注意 「あ-ん」では全てのひらがなが変換されるわけではありません）
$ echo ぁあいうえおゔ | tr "あ-ん" "ア-ン"
ぁアイウエオゔ

# Linux (GNU版) （Unicode 非対応）
$ echo ぁあいうえおゔ | tr "あ-ん" "ア-ン"
]��]�]]��]��]��]��]]�
```

## `uconv` コマンドの例

結構いろんな変換ができます。この記事ではすべてを紹介しているわけではないので、`-x` オプションで指定する変換規則 (Transliteration) についての詳細は「[Transforms | ICU Documentation](https://unicode-org.github.io/icu/userguide/transforms/general/)」を参照してください。

### 文字コードの変換

`uconv` は `iconv` や `nkf` と同じように文字コードの変換を行うことができます。文字コードの変換として使う場合、オプションは `iconv` と互換性があるように設計されており簡単に置き換えることができます。もちろん Shift JIS や EUC-JP だけではなく、世界中の文字コードに対応しています。`uconv -l` で対応している文字コード一覧を出力することができます。

```console
$ echo あいう | uconv -f UTF-8 -t SHIFT_JIS | nkf --guess
Shift_JIS (LF)

$ uconv -l # 対応している文字コード一覧を出力
（大量にあるので省略）
```

注意 `nkf --guess` は変換後の文字コードの確認のためだけに使用しています。

### 半角を全角に変換

```console
$ echo ｱｲｳｴｵabcde | uconv -x "Halfwidth-Fullwidth"
アイウエオａｂｃｄｅ
```

### 全角を半角に変換

```console
$ echo アイウエオ | uconv -x "Fullwidth-Halfwidth"
ｱｲｳｴｵ
```

### カタカナのみを半角から全角に変換

Unicode プロパティが使用できます。

```console
$ echo ｱｲｳｴｵabcde | uconv -x "\p{katakana} Halfwidth-Fullwidth"
アイウエオabcde
```

### 大文字に変換

```console
$ echo abcde | uconv -x "upper"
ABCDE
```

### ひらがなをカタカナに変換

```console
$ echo あいうえお | uconv -x katakana
アイウエオ
```

### カタカナをひらがなに変換

```console
$ echo アイウエオ | uconv -x hiragana
あいうえお
```

### 指定した範囲のみの変換

```console
$ echo ABCDEABCDE | uconv -x "[A-C] lower"
abcDEabcDE
```

### 複数の変換を一度に行う

大文字変換⇒全角変換といった一連の変換も一コマンドで実行できる。

**注意** 単純に複数の変換を繋げられるわけではないようです。何をどう組み合わせられるのかよく分かっていません。

```console
$ echo abcde | uconv -x "upper; Halfwidth-Fullwidth"
ＡＢＣＤＥ
```

### 特定の文字を変換

```console
$ echo abc | uconv -x 'b>ビー'
aビーc
```

### 文字削除（tr -d 相当）

```console
$ echo abcde | uconv -x '[b-d] remove'
ae
```

以下の方法でも可能

```console
$ echo abcde | uconv -x '[b-d] >;'
ae
```

### 最初の一文字だけ大文字化

```console
echo abc | uconv -x 'title'
Abc
```

以下の方法でも可能

```console
echo abc | uconv -x '^(.) > &upper($1)'
Abc
```

### 文字単位の変換

文字の順番を入れ替え、さらに片方だけ大文字化しています。

```console
$ echo abcd | uconv -x '(.)(.) > &upper($2)$1'
BaDc
```

### ローマ字変換

```console
$ echo aiueo | uconv -x hiragana
あいうえお
```

### 制御文字を名前で出力

```console
$ printf 'a\tb' | uconv -x '\p{Cc} any-name'
a\N{<control-0009>}b%
```

**注意** 最後の `%` は出力が改行で終わらない場合に zsh がそれを知らせるためにつけているものです。

### Unicode 正規化 (NFD, NFC, NFKD, NFKC)

[Unicode 正規化](https://ja.wikipedia.org/wiki/Unicode%E6%AD%A3%E8%A6%8F%E5%8C%96) を行うことが出来ます。

```console
$ echo ｶﾞガ | uconv -x NFD # ef bd b6   ef be 9e   e3 82 ab   e3 82 99
ｶﾞガ
$ echo ｶﾞガ | uconv -x NFC # ef bd b6   ef be 9e   e3 82 ac
ｶﾞガ
$ echo ｶﾞガ | uconv -x NFKD # e3 82 ab   e3 82 99   e3 82 ab   e3 82 99
ガガ
$ echo ｶﾞガ | uconv -x NFKC # e3 82 ac   e3 82 ac
ガガ
```

と言いたいところなのですが、2023-06-15: ICU 73.2 時点で（おそらく）バグにより NFC / NFKC が機能しない文字があります。

```console
$ # 「が」はNFCできる
$ printf が | uniname
character  byte       UTF-32   encoded as     glyph   name
        0          0  00304C   E3 81 8C       が      HIRAGANA LETTER GA
$ printf が | uconv -x nfd | uconv -x nfc | uniname
character  byte       UTF-32   encoded as     glyph   name
        0          0  00304C   E3 81 8C       が      HIRAGANA LETTER GA

$ # 「ガ」はNFCできない
$ printf ガ | uniname
character  byte       UTF-32   encoded as     glyph   name
        0          0  0030AC   E3 82 AC       ガ      KATAKANA LETTER GA
$ printf ガ | uconv -x nfd | uconv -x nfc | uniname
character  byte       UTF-32   encoded as     glyph   name
        0          0  0030AB   E3 82 AB       カ      KATAKANA LETTER KA
        1          3  003099   E3 82 99       ゙      COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
```

カタカナの濁点の場合がだめとか言うわけではなく、特定の文字だけ NFC / NFKC が機能しないので謎です。以下の [ ] の文字が正しく機能しない文字です。

```txt
がぎぐげござじずぜぞだぢづで[ど][ば][ぱ]びぴぶぷ[べ][ぺ]ぼぽゔゞ
[ガ][ギ][グ]ゲゴザジ[ズ][ゼ][ゾ][ダ]ヂヅデドバパビピブプベペボポヴ[ヷ]ヸヹヺ[ヾ]
```

NFC / NFKC を使いたい場合は、Perl か Ruby を使うのがおすすめです。どちら標準モジュールで動作し、短く書くことが出来ます。

```console
$ printf ガ | uconv -x nfd | perl -MUnicode::Normalize -CS -pe '$_=NFC($_)' | uniname
character  byte       UTF-32   encoded as     glyph   name
        0          0  0030AC   E3 82 AC       ガ      KATAKANA LETTER GA

$ printf ガ | uconv -x nfd | ruby -pe '$_.unicode_normalize!(:nfc)' | uniname
character  byte       UTF-32   encoded as     glyph   name
        0          0  0030AC   E3 82 AC       ガ      KATAKANA LETTER GA
```

## インストール方法

### macOS

```sh
brew install icu4c
```

.zshrc などで環境変数 PATH を設定する

```sh
export PATH="/usr/local/opt/icu4c/bin:$PATH"
```

### Debian

```sh
sudo apt install icu-devtools
```

### RedHat

```sh
sudo yum install icu
```

## 参考リンク

- [uconvコマンドを使ってローマ字⇔ひらがな/カタカナ変換をする](https://orebibou.com/ja/home/201802/20180203_001/)
- [シェルで全角/半角/大文字/小文字/ひらがな/カタカナ/英字の変換を行うためにuconvを使う](https://genzouw.com/entry/2020/02/05/194537/1913/)