ShellScript
nkf
urlエンコード
2

シェルスクリプトでシンプルにurlエンコードする話

訳あってシェルスクリプト上で文字列を符号化したかったのですが、結構色んな所で詰まったのでまとめました。

やってること自体はシンプルですが、簡単ではないです。

tl;dr

↓の関数のような処理を行えばOKです。

function urlencode {
  echo "$1" | nkf -WwMQ | sed 's/=$//g' | tr = % | tr -d '\n'
}

使用例

% urlencode "記号入りの文字列<>&abc"
%E8%A8%98%E5%8F%B7%E5%85%A5%E3%82%8A%E3%81%AE%E6%96%87%E5%AD%97%E5%88%97%3C%3E%26abc

各処理の説明

1つずつ処理の流れを説明していきます。

nkf -WwMQ

Googleで「シェルスクリプト URLエンコード」などで調べるとnkfを使うという方法がいくつか紹介されています。
- urlエンコード(コマンドライン/シェルスクリプト) - Qiita
- 逆引きUNIXコマンド/URLエンコード・URLデコード方法・nkf - Linuxと過ごす

このnkfのオプションはそれぞれ以下の指定となっています。

OPTIONS
       -J -S -E -W -W16 -W32 -j -s -e -w -w16 -w32
           Specify input and output encodings. Upper case is input.  cf. --ic and --oc.
           -W  UTF-8N.


       -M  MIME encode. Header style. All ASCII code and control characters are intact.

           -MQ Perform quoted encoding.

すなわち、nkfの入力出力の文字コードを、共にUTF-8を指定、quoted encodingした上で出力するという指定になります。

quoted encodingは主に電子メールで非ASCII文字を表現するために使用するエンコード方式です。 任意の8ビットバイト値を =[16進数2桁] の表記にすることでASCII範囲内の文字列で表現しています。

ただ、本来のquoted encodingではASCII印字可能文字は= 以外符号化不要のはずなのですが、このnkf オプションではアルファベット:数次以外のASCII印字可能文字も符号化されます。(そのおかげでurlエンコードできるのですが)

これでアルファベット・数次以外の文字をすべて符号化出来ます。

sed 's/=$//g'

Quoted-printable で符号化したデータの行は、76文字を超えてはならない。

このような成約があるため、nkfに長い文字列を渡した場合、以下のように改行されて出力されます。

% echo "とても長い文章ああ" |nkf -WwMQ
=E3=81=A8=E3=81=A6=E3=82=82=E9=95=B7=E3=81=84=E6=96=87=E7=AB=A0=E3=81=82=
=E3=81=82

URLエンコード目的では改行は不要のため、改行を無視して扱う必要があります。(それ自体は後のステップで行います)

さらにここで注意しなければならないのは、その挿入された改行を明記するために行末に=が追加されます。
単純に改行を削除してしまうだけだと、この=が次の行とつながり == のような仕様上正しくない文字順が生まれてしまいます。

なので、各行の末尾に存在する =をこのsedで削除します。

tr = %

Quoted-printableは =?? という表記で符号化していますが、URLエンコーディングでは %?? という表記で符号化しています。

そのため、出力結果の=% に置換します。

tr -d '\n'

前述のように、Quoted-printableでは76文字を超える文字列は禁止のため、主力結果が76文字を超える場合、nkfで改行が挿入されています。

しかし、基本的にURLエンコーディングではそういった制約はなく、改行は不要のため、trを用いて改行を削除しています。