web-dev-qa-db-ja.com

シェルでURLエンコードされた文字列をデコードするにはどうすればよいですか?

エンコードされたユーザーエージェントのリストを含むファイルがあります。例えば。:

Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B%20en

このファイルを読み取り、デコードされた文字列で新しいファイルに書き込むことができるシェルスクリプトが必要です。

Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en

私はこの例を使ってそれを実現しようとしましたが、今のところうまくいきません。

$ echo -e "$(echo "%31+%32%0A%33+%34" | sed 'y/+/ /; s/%/\\x/g')"

私のスクリプトは次のようになります:

#!/bin/bash
for f in *.log; do
  echo -e "$(cat $f | sed 'y/+/ /; s/%/\x/g')" > y.log
done
35
user785717

簡単な1行のソリューションを次に示します。

$ urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

Perlのように見えるかもしれません:)しかし、それは単なるbashです。 awks、seds、オーバーヘッドなし。 :ビルトイン、特殊パラメーター、パターン置換、およびエコービルトインの-eオプションを使用して、16進コードを文字に変換します。詳細については、bashのマンページを参照してください。この関数を個別のコマンドとして使用できます

$ urldecode https%3A%2F%2Fgoogle.com%2Fsearch%3Fq%3Durldecode%2Bbash
https://google.com/search?q=urldecode+bash

または、変数の割り当てなどで:

$ x="http%3A%2F%2Fstackoverflow.com%2Fsearch%3Fq%3Durldecode%2Bbash"
$ y=$(urldecode "$x")
$ echo "$y"
http://stackoverflow.com/search?q=urldecode+bash
44
guest

GNU awk

#!/usr/bin/awk -fn
@include "ord"
BEGIN {
  RS = "%.."
}
{
  printf RT ? $0 chr("0x" substr(RT, 2)) : $0
}

または

#!/bin/sh
awk -niord '{printf RT?$0chr("0x"substr(RT,2)):$0}' RS=%..

awk printfを使用してテキストをurldecodeする

18
Steven Penny

BASHを使用して、エンコードされたURLを標準入力から読み取り、デコードするには:

while read; do echo -e ${REPLY//%/\\x}; done

押す CTRL-D ファイルの終わり(EOF)を通知し、正常に終了します。

ファイルを以下の標準に設定することにより、ファイルの内容をデコードできます。

while read; do echo -e ${REPLY//%/\\x}; done < file

たとえば、パイプからの入力をデコードできます。

echo 'a%21b' | while read; do echo -e ${REPLY//%/\\x}; done
  • Read組み込みコマンドは、改行文字が表示されるまで標準入力を読み取ります。 REPLYという変数を、読み取ったばかりのテキスト行に等しく設定します。
  • ${REPLY//%/\\x}は、「%」のすべてのインスタンスを「\ x」に置き換えます。
  • echo -eは、\xNNをASCII 16進値NNの文字として解釈します。
  • whileは、読み取りコマンドが失敗するまでこのループを繰り返します。 EOFに達しました。

上記は「+」を「」に変更しません。ゲストの answer のように、「+」を「」にも変更するには:

while read; do : "${REPLY//%/\\x}"; echo -e ${_//+/ }; done
  • :はBASH組み込みコマンドです。ここでは、単一の引数を取り、それに対して何もしません。
  • 二重引用符は、1つのパラメーター内にすべてを作ります。
  • _は、引数の展開後、前のコマンドの最後の引数に等しい特別なパラメーターです。これはREPLYの値で、 '%'のすべてのインスタンスが '\ x'に置き換えられます。
  • ${_//+/ }は、 '+'のすべてのインスタンスを ''に置き換えます。

これはBASHのみを使用し、ゲストの回答と同様に他のプロセスを開始しません。

11
brendan

これは私のために働いているようです。

#!/bin/bash
urldecode(){
  echo -e "$(sed 's/+/ /g;s/%\(..\)/\\x\1/g;')"
}

for f in /opt/logs/*.log; do
    name=${f##/*/}
    cat $f | urldecode > /opt/logs/processed/$HOSTNAME.$name
done

'+'をスペースに、%記号を '\ x'エスケープに置き換え、エコーに '-e'オプションを使用して\ xエスケープを解釈させていませんでした。何らかの理由で、catコマンドは%記号を独自のエンコード形式%25として出力していました。したがって、sedは単に%25を\ x25に置き換えていました。 -eオプションを使用した場合、単に\ x25を%として評価し、出力は元のものと同じでした。

トレース:

オリジナル:Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B%20en

sed:Mozilla\x252F5.0\x2520\x2528Macintosh\x253B\x2520U\x253B\x2520Intel\x2520Mac\x2520OS\x2520X\x252010.6\x253B\x2520en

echo -e:Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B% 20en

修正:基本的に、sedの%の後の2文字を無視します。

sed:Mozilla\x2F5.0\x20\x28Macintosh\x3B\x20U\x3B\x20Intel\x20Mac\x20OS\x20X\x2010.6\x3B\x20en

echo -e:Mozilla/5.0(Macintosh; U; Intel Mac OS X 10.6; en

広範なテストの後、これがどのような複雑さをもたらすかはわかりませんが、今のところは機能します。

11
user785717

あなたがpython開発者である場合、これはおそらくより好ましい

echo "%21%20" | python -c "import sys, urllib as ul; print ul.unquote(sys.stdin.read());"

rllib はそれを処理するのにプロです。

10
Jay
_Perl -pi.back -e 'y/+/ /;s/%([\da-f]{2})/pack H2,$1/gie' ./*.log
_

_-i_を使用すると、ファイルがインプレースで更新されます(一部のsed実装は、Perlからそれを借用します)。バックアップ拡張機能として_.back_を使用します。

_s/x/y/e_はxを ey Perlコードの評価。

この場合のPerlコードは、packを使用して、_$1_(正規表現の最初の括弧のペア)でキャプチャされた16進数を対応する文字としてパックします。

packの代わりにchr(hex($1))を使用することもできます:

_Perl -pi.back -e 'y/+/ /;s/%([\da-f]{2})/chr hex $1/gie' ./*.log
_

利用可能な場合、_URI::Escape_のuri_unescape()も使用できます。

_Perl -pi.back -MURI::Escape -e 'y/+/ /;$_=uri_unescape$_' ./*.log
_
7

ネイティブBashで実行するためのBashスクリプト( 元のソース ):

LANG=C

urlencode() {
    local l=${#1}
    for (( i = 0 ; i < l ; i++ )); do
        local c=${1:i:1}
        case "$c" in
            [a-zA-Z0-9.~_-]) printf "$c" ;;
            ' ') printf + ;;
            *) printf '%%%.2X' "'$c"
        esac
    done
}

urldecode() {
    local data=${1//+/ }
    printf '%b' "${data//%/\x}"
}

ファイルコンテンツをurldecodeする場合は、ファイルコンテンツを引数として入力します。

エンコードされたエンコード済みファイルの内容が異なる場合に停止するテストを次に示します(数秒間実行すると、スクリプトはおそらく正しく動作します)。

while true
  do cat /dev/urandom | tr -d '\0' | head -c1000 > /tmp/tmp;
     A="$(cat /tmp/tmp; printf x)"
     A=${A%x}
     A=$(urlencode "$A")
     urldecode "$A" > /tmp/tmp2
     cmp /tmp/tmp /tmp/tmp2
     if [ $? != 0 ]
       then break
     fi
done
6
Janus Troelsen

サーバーにphpがインストールされている場合は、URLエンコードされた文字列を使用して、任意のファイルを「cat」または「tail」できます。

tail -f nginx.access.log | php -R 'echo urldecode($argn)."\n";'
4
Oleg Bondar'

@ barti_dd がコメントで述べたように、\x「[ダブル]エスケープする必要があります」。

% echo -e "$(echo "Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B%20en" | sed 'y/+/ /; s/%/\\x/g')"
Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en

Bashとsedを混同するのではなく、これをすべてPythonで行います。大まかな方法​​は次のとおりです。

#!/usr/bin/env python

import glob
import os
import urllib

for logfile in glob.glob(os.path.join('.', '*.log')):
    with open(logfile) as current:
        new_log_filename = logfile + '.new'
        with open(new_log_filename, 'w') as new_log_file:
            for url in current:
                unquoted = urllib.unquote(url.strip())
                new_log_file.write(unquoted + '\n')
4
Johnsyweb

GNU awkの場合:

gawk -vRS='%[0-9a-fA-F]{2}' 'RT{sub("%","0x",RT);RT=sprintf("%c",strtonum(RT))}
                             {gsub(/\+/," ");printf "%s", $0 RT}'
3

更新 ジェイズ Python 3.5+:
echo "%31+%32%0A%33+%34" | python -c "import sys; from urllib.parse import unquote ; print(unquote(sys.stdin.read()))"

それでも、 brendan's 説明付きのbashソリューションはより直接的でエレガントに見えます。

2
yemiteliyadu

これは、入力と出力がbash変数である純粋なbashで行われる解決策です。スペースとして「+」をデコードし、「%20」スペースと他の%エンコード文字を処理します。

#!/bin/bash
#here is text that contains both '+' for spaces and a %20
text="hello+space+1%202"
decoded=$(echo -e `echo $text | sed 's/+/ /g;s/%/\\\\x/g;'`)
echo decoded=$decoded
2
nevertooloud
$ uenc='H%C3%B6he %C3%BCber%20dem%20Meeresspiegel'
$ utf8=$(echo -e "${uenc//%/\\x}")
$ echo $utf8
Höhe über dem Meeresspiegel
$
2
guest

https://stackoverflow.com/a/37840948/814247 に展開
HTMLエンティティを操作する

$ htmldecode(){: "$ {* // + /}"; echo -e "$ {_ //#x/\ x}" | tr -d ';'; }
$ htmldecode "http#x3A;#x2F;#x2F; google.com#x2F; search#x3F; q#x3D; urldecode#x2B; bash" http://google.com/ search?q = urldecode + bash

(引数を引用する必要があります)

0
Calvin Kim