シェルスクリプトで標準入力から1文字ずつ取り出して処理したい

Bash

はじめに

標準入力から1文字ずつ取り出して処理したい場合にどのように書いたらよいかについて。
今まで使っていた fold では問題があったのでもう一度勉強し直した。

検証環境

$ uname -moi
x86_64 MacBookPro10,1 Darwin

$ bash -version
GNU bash, バージョン 5.0.7(1)-release (x86_64-apple-darwin18.5.0)

foldを使う

fold コマンドは、テキストファイルなどを読み込み、指定した幅で改行して出力するコマンドです。
改行文字を「挟み込む」とイメージするとよいでしょう。

これと -w 1 オプションを使って標準入力の内容を1文字ずつに分割できます。

$ echo "abcdefg"
abcdefg

$ echo "abcdefg" | fold -w 1 | while read c; do
  echo "[${c}]"
done
[a]
[b]
[c]
[d]
[e]
[f]
[g]

foldでは全角文字に対応できない

今までfoldを使っていたのですが、こちら全角文字に対応できないようです。
(幸い全角文字を取り扱うケースがあまりなかったようです。)

$ echo "abcあいeう fg" | fold -w 1 | while read c; do   echo "[${c}]"; done
[a]
[b]
[c]
[]
[]
[]
[]
[]
[]
[e]
[]
[]
[]
[f]
[g]

grepを使う

grep-o オプションというものがあります。
grep は通常条件にマッチした行を出力しますが、 -o オプションを付けると条件にマッチした 部分 だけを出力することができます。

以下の例では、HTML内のタグのみを抽出しています。

cat <<'EOF' > colors.html
<html>
     <body>
          <div>
               <h2>いろ</h2>
               <ul>
                    <li>あお</li>
                    <li>あか</li>
                    <li>きいろ</li>
               </ul>
          </div>
     </body>
</html>
EOF

# タグのみを抽出する
$ cat colors.html | grep -o '<[^>]\+>'
<html>
<body>
<div>
<h2>
</h2>
<ul>
<li>
</li>
<li>
</li>
<li>
</li>
</ul>
</div>
</body>
</html>

この機能を使って、「1文字」ごとに改行表示させることができます。

$ echo "abcあいeう fg" | grep -o . | while read c; do   echo "[${c}]"; done
[a]
[b]
[c]
[あ]
[い]
[e]
[う]
[]
[f]
[g]

grep -o .でも全角文字がうまく扱えない?

grep -o . の方法ですが、こちらも環境ごとに動作が違うようです。

DockerのCentOS7環境では fold -w 1 と同じ動作をしました。
こちら、日本語のロケールが設定されていないことが原因で、以下のコマンドを実行することで解消されました。

yum reinstall -y glibc-common
localectl set-locale LANG=ja_JP.UTF-8
source /etc/locale.conf
localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8

export LC_ALL=ja_JP.UTF-8

CentOS7系環境では、 fold -w 1 も全角を正しく扱える

実は、CentOS7環境でロケール環境を構築したあとでは、 fold -w 1 も正しく動作することがわかりました。

$ echo "abcあいeう fg" | fold -w 1 | while read c; do   echo "[${c}]"; done
[a]
[b]
[c]
[あ]
[い]
[e]
[う]
[]
[f]
[g]

とはいえ、環境ごとに動作がことなることを避けるため、ロケールの設定を正しく行った上で grep -o . を使う、という方法が良さそうですね。

ひとこと

「シェル芸勉強会」に参加して勉強になりました。

Bash