この記事は
YAMAP エンジニア Advent Calendar 2020 の九日目になる予定です。
tl;dr
URL エンコードされた文字列を、コマンドラインでシュッとデコードしたかったので調べたのでメモ。
URL エンコードされる文字列は以下の通り。
負けない事 投げ出さない事 逃げ出さない事
これを URL エンコードすると、以下のような文字列となる。
%E8%B2%A0%E3%81%91%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E6%8A%95%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E9%80%83%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B
本記事で利用する環境は以下の通り。
root@81301d0ba685:/# cat /etc/os-release PRETTY_NAME="Debian GNU/Linux 10 (buster)" NAME="Debian GNU/Linux" VERSION_ID="10" VERSION="10 (buster)" VERSION_CODENAME=buster ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/"
なお、以下の投稿が、知見の泉だったので、この投稿を読むだけでもとても楽しかったです。
- bash - How to decode URL-encoded string in shell? - Stack Overflow
- Bash urlencode and urldecode · GitHub
シュッと
printf でやってみる
ということで、上述の投稿を参考に。
$ str=${ENCODED_STRING} $ printf '%b\n' "${str//%/\\x}"
printf だけで出来るのが衝撃的だった。
以下、実行例。
root@ebfcf7226ae2:/# str='%E8%B2%A0%E3%81%91%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E6%8A%95%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E9%80%83%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B' root@ebfcf7226ae2:/# printf '%b\n' "${str//%/\\x}" 負けない事 投げ出さない事 逃げ出さない事
おおー。
nkf でやってみる
往年の名機、nkf でも簡単に。
$ echo ${ENCODED_STRING} | nkf --url-input
以下、実行例。
root@81301d0ba685:/# echo '%E8%B2%A0%E3%81%91%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E6%8A%95%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E9%80%83%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B' | nkf --url-input 負けない事 投げ出さない事 逃げ出さない事
おおー。これも簡単。
$ nkf --help | grep url --{cap, url}-input Convert hex after ':' or '%'
Ruby でやってみる
手元の環境に Ruby をインストールしなければいけないという点で、少しだけハードルが上がってしまうけど、十分、シュッとソラで書けるような気がする。
# 利用する Ruby のバージョン $ ruby --version ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux] # warning: URI.unescape is obsolete となる $ echo ${ENCODED_STRING} | ruby -r 'uri' -e 'puts URI.unescape(STDIN.read)' # こちらを利用する $ echo ${ENCODED_STRING} | ruby -r 'cgi' -e 'puts CGI.unescape(STDIN.read)'
以下、実行例。
$ echo '%E8%B2%A0%E3%81%91%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E6%8A%95%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E9%80%83%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B' | ruby -r 'uri' -e 'puts URI.unescape(STDIN.read)' 負けない事 投げ出さない事 逃げ出さない事 $ echo '%E8%B2%A0%E3%81%91%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E6%8A%95%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E9%80%83%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B' | ruby -r 'cgi' -e 'puts CGI.unescape(STDIN.read)' 負けない事 投げ出さない事 逃げ出さない事 # Docker イメージを利用すれば、Ruby を直接インストールが不要 $ echo '%E8%B2%A0%E3%81%91%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E6%8A%95%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E9%80%83%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B' | docker run -i --rm ruby ruby -r 'cgi' -e 'puts CGI.unescape(STDIN.read)' 負けない事 投げ出さない事 逃げ出さない事
PHP でやってみる
Ruby と同様に手元にインストールが必要という点では...(略。十分、サラッと書けそう。
# 利用する PHP のバージョン $ php --version PHP 8.0.0 (cli) (built: Dec 1 2020 03:14:26) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies $ echo ${ENCODED_STRING} | php -R 'echo urldecode($argn),"\n";'
以下、実行例。
$ echo '%E8%B2%A0%E3%81%91%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E6%8A%95%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E9%80%83%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B' | php -R 'echo urldecode($argn),"\n";' 負けない事 投げ出さない事 逃げ出さない事 $ echo '%E8%B2%A0%E3%81%91%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E6%8A%95%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B%0D%0A%E9%80%83%E3%81%92%E5%87%BA%E3%81%95%E3%81%AA%E3%81%84%E4%BA%8B' | docker run --rm -i php php -R 'echo urldecode($argn),"\n";' 負けない事 投げ出さない事 逃げ出さない事
パイプで渡された値を PHP で処理する場合には、-R
オプションを利用すると良さそう。
$ php --help | grep '\-R' ... 略 ... -R <code> Run PHP <code> for every input line
以上
一番、簡単そうなのは、
$ printf '%b\n' "${str//%/\\x}"
なんだけど、なぜ、${str//%/\\x}
だけでデコードしてくれるのか、ちゃんと理解出来ていないので、なんだか気持ち悪い (man
してみたりしたけど、よく解らなかった) ので、コマンドライン上で実現したい場合には、nkf
なのかなーと思っています。