it-swarm.com.de

Wie decodiert man eine URL-kodierte Zeichenfolge in der Shell

Ich habe eine Datei mit einer Liste von Benutzeragenten, die verschlüsselt sind . E.g .:

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

Ich möchte ein Shell-Skript, das diese Datei lesen und in eine neue Datei mit dekodierten Zeichenfolgen schreiben kann.

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

Ich habe versucht, dieses Beispiel zu verwenden, um es in Gang zu bringen, aber es funktioniert bisher nicht.

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

Mein Skript sieht so aus:

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

Hier ist eine einfache einzeilige Lösung. 

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

Es mag wie Perl aussehen :), aber es ist nur pure Bash. Keine Felle, keine Seds ... keine Gemeinkosten. Verwenden Sie: builtin, spezielle Parameter, Mustersubstitution und die Option -e des Echo Builtins, um Hex-Codes in Zeichen zu übersetzen. Weitere Informationen finden Sie in der Manpage von bash. Sie können diese Funktion als separaten Befehl verwenden

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

oder in variablen Zuweisungen, wie so:

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

GNU awk

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

Oder

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

Verwenden von awk printf zum Urldecode von Text

18
Steven Penny

С BASH, чтобы прочитать процентный кодированный URL из стандартного ввода и декодировать:

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

НажмитеCTRL-Dсигнализировать об окончании файла (EOF) und корректно завершить работу.

Weitere Informationen zum Thema:

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

Weitere Informationen zum Thema:

echo 'a%21b' | while read; do echo -e ${REPLY//%/\\x}; done
  • Встроенная команда read читает стандарт до тех пор, пока не увидит символ перевода строки. Он устанавливает переменную с именем REPLY равной строке текста, которую он только что прочитал.
  • ${REPLY//%/\\x} заменяет все экземпляры "%" на "\ x".
  • echo -e интерпретирует \xNN как символ ASCII с шестнадцатеричным значением NN.
  • während des Vorgangs wird ein Fehler angezeigt, und der Vorgang wird abgebrochen, während der Vorgang abgebrochen wird, wird ein Fehler angezeigt. EOF достигнуто.

Вышеуказанное не меняет "+" на "". Чтобы изменить '+' на '' также, как в гостевой ответ :

while read; do : "${REPLY//%/\\x}"; echo -e ${_//+/ }; done
  • : - встроенная команда BASH. Здесь он просто принимает один аргумент и ничего не делает с ним.
  • Двойные кавычки делают все внутри одного параметра.
  • _ - это специальный параметр. Это значение REPLY со всеми экземплярами "%", замененными на "\ x".
  • ${_//+/ } заменяет все экземпляры '+' на ''.

При этом используется только BASH и не запускается никакой другой процесс, аналогичный ответу гостя.

11
brendan

Das scheint für mich zu funktionieren.

#!/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

Das Ersetzen von '+' durch Leerzeichen und% -Zeichen durch '\ x'-Escapezeichen, und das Echo der\x-Escapezeichen mit der' -e'-Option interpretieren zu lassen, funktionierte nicht. Aus irgendeinem Grund druckte der Befehl cat das% -Zeichen als eigenes codiertes Formular% 25. Sed hat also einfach% 25 durch\x25 ersetzt. Wenn die Option -e verwendet wurde, wurde\x25 einfach als% ausgewertet, und die Ausgabe entsprach der des Originals.

Trace:

Original: 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

Fix: Ignoriere grundsätzlich die 2 Zeichen nach dem% in sed.

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; de 

Ich bin nicht sicher, welche Komplikationen dies nach umfangreichen Tests zur Folge haben würde, aber es funktioniert im Moment.

9
user785717

wenn Sie ein python - Entwickler sind, ist dies möglicherweise vorzuziehen 

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

urllib ist professionell im Umgang damit

8
Jay

Bash-Skript für die Verwendung in nativen Bash ( Originalquelle ):

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

Wenn Sie den Inhalt der Datei urldecodeen möchten, geben Sie den Inhalt der Datei einfach als Argument an.

Hier ist ein Test, der angehalten wird, wenn sich der Inhalt der decodierten codierten Datei unterscheidet (wenn er einige Sekunden läuft, funktioniert das Skript wahrscheinlich richtig)

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
Perl -pi.back -e 'y/+/ /;s/%([\da-f]{2})/pack H2,$1/gie' ./*.log

Mit -i werden die Dateien vor Ort aktualisiert (einige sed-Implementierungen haben diese aus Perl ausgeliehen) mit .back als Sicherungserweiterung.

s/x/y/e ersetzt x durch die ebewertung des y Perl-Codes.

Der Perl-Code verwendet in diesem Fall pack, um die in $1 erfasste Hex-Zahl (erstes Klammerpaar im regulären Ausdruck) als entsprechendes Zeichen zu packen.

Eine Alternative zu pack ist die Verwendung von chr(hex($1)):

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

Falls verfügbar, können Sie auch uri_unescape() aus URI::Escape verwenden:

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

Wenn Sie php auf Ihrem Server installiert haben, können Sie jede Datei sehr einfach "cat" oder sogar "tail" machen.

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

Wie @barti_ddu in den Kommentaren sagte, sollte \x "[double-] escaped sein".

% 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

Anstatt Bash und Sed zu mischen, würde ich das alles in Python machen. Hier ist ein grober Schnitt wie:

#!/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

Mit GNU awk:

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

Aktualisierung Jays Antwort für Python 3.5+:
echo "%31+%32%0A%33+%34" | python -c "import sys; from urllib.parse import unquote ; print(unquote(sys.stdin.read()))"

Trotzdem scheint brendans bash Lösung mit Erklärung direkter und eleganter.

2
yemiteliyadu

Hier ist eine Lösung, die in reinen Bashs ausgeführt wird, wobei Eingabe und Ausgabe Bash-Variablen sind. Es dekodiert '+' als Leerzeichen und behandelt das '% 20'-Leerzeichen sowie andere% -kodierte Zeichen. 

#!/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

Erweiterung zu https://stackoverflow.com/a/37840948/8142470
um mit HTML-Entitäten zu arbeiten

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

(Argument muss zitiert werden)

0
Calvin Kim