シェルスクリプトでマルチバイト文字の文字数を確認する際の注意事項(おまけでJSTなど)

Bash,CentOS,Docker,Linux

はじめに

Bashシェルスクリプトで日本語の全角文字(ex: あいう)の文字数を取得したかったのですが環境の設定に注意しないといけないことがありました。

検証環境

$ uname -moi
x86_64 x86_64 GNU/Linux

$ bash -version | head -n 1
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

マルチバイト文字のを含む文字列の「バイト数」「文字数」を取得

マルチバイト文字のを含む文字列の「バイト数」「文字数」をそれぞれ取得してみたいと思いました。

クリーンな環境が欲しかったので、 docker runCentOS7 環境を利用してみました。

# 以下のコマンドでCentOS7上のログインシェルが起動
$ docker run --rm -it centos:7

[root@fbdeab6f7982 /]#

ここで、 あいう という全角3文字のバイト数、文字数を取得してみます。
それぞれ wc -cwc -m というコマンドに対してパイプ経由で文字列を渡してやります。

# -nオプション無しでechoすると改行文字が含まれてしまうことに注意!
$ echo あいう | wc -c
10

# これで改行が除外される(プロンプトがおかしな場所に出力されていることからわかる)
$ echo -n あいう
あいう$

# wc -c でバイト数を取得
$ echo -n あいう | wc -c
9

# wc -c で文字数を取得
$ echo -n あいう | wc -m
9

# 変数に代入して、変数の文字列長を取得する命令を利用してみる
$ X=あいう
$ echo ${#X}
9

「バイト数」は想定通りでしたが、「文字数」が正しく取得できません。(バイト数と同じ値になってしまいました。)

LANG 環境変数を設定することで対処

解決方法からいうと、 LANG 環境変数を設定してやることで問題が解決できます。
面白いのは、日本語ロケール値( ja_JP.utf8)を設定しなくても解決できるということです。

試してみます。

# localeコマンドで利用できるロケールを確認できる
$ locale -a | head
C
POSIX
en_AG
en_AG.utf8
en_AU
en_AU.iso88591
en_AU.utf8
en_BW
en_BW.iso88591
en_BW.utf8

# 日本語ロケールは用意されていない
$ locale -a | grep -i ja_jp

# 英語ロケールは用意されている
$ locale -a | grep -i en_us
en_US
en_US.iso88591
en_US.iso885915
en_US.utf8

# 環境変数 LANG を設定
$ export LANG=en_US.utf8

# 「バイト数」
$ echo -n あいう | wc -c
9

# 「文字数」が正しく出力される!
$ echo -n あいう | wc -m
3

# 「文字数」が正しく出力される!
$ echo ${#X}
3

このようにマルチバイト文字を考慮した文字数が取得できるようになりました。

おまけ:日本語ロケールを導入する

date コマンドなどで日本語表記したい場合は、 localedef を実行した後、環境変数 LANG を設定してやります。

# 英語表記となっている
$ date
Tue Mar  3 23:27:18 UTC 2020

# 日本語ロケール導入
$ localedef -f UTF-8 -i ja_JP ja_JP.UTF-8

# この状態ではまだ英語表記
$ date
Tue Mar  3 23:29:08 UTC 2020

# LANG環境変数を設定する
$ locale -a | grep -i ja
ja_JP.utf8
$ export LANG=ja_JP.utf8

# 日本語表記となりました
$ date
2020年  3月  3日 火曜日 23:29:22 UTC

さらにタイムゾーンを JST に変更したい場合は TZ 環境変数を設定しましょう。

$ export TZ=Asia/Tokyo
[root@4922b3027128 /]# date
2020年  3月  4日 水曜日 08:54:15 JST

ひとこと

Dockerイメージ作成の際に日本語ローカライズしたい場合にどうぞ。

Bash,CentOS,Docker,Linux