ようへいの日々精進XP

よかろうもん

コマンドラインで URL デコードする一例

この記事は

YAMAP エンジニア Advent Calendar 2020 の九日目になる予定です。

qiita.com

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/"

なお、以下の投稿が、知見の泉だったので、この投稿を読むだけでもとても楽しかったです。

シュッと

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)'

docs.ruby-lang.org

docs.ruby-lang.org

以下、実行例。

$ 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";'

www.php.net

以下、実行例。

$ 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 なのかなーと思っています。

参考