Как декодировать строку в кодировке URL в оболочке?

У меня есть файл со списком пользовательских агентов, которые закодированы. Например:.

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

Мне нужна оболочка script, которая может читать этот файл и записывать в новый файл с декодированными строками.

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

Мой script выглядит так:

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

Ответ 2

Вот простое однострочное решение.

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

Он может выглядеть как perl:), но он просто чистый bash. Никаких awks, никакие seds... никакие накладные расходы. Использование: встроенные, специальные параметры, подстановка шаблона и встроенная опция echo для перевода шестнадцатеричных кодов в символы. Для получения дополнительной информации см. Справочную страницу 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

Ответ 3

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

С -i обновляет файлы на месте (некоторые sed реализации заимствовали это из perl) с .back в качестве расширения для резервного копирования.

s/x/y/e заменяет x оценкой e кода y perl.

В этом случае perl-код использует pack для упаковки шестнадцатеричного числа, записанного в $1 (первая пара скобок в регулярном выражении) в качестве соответствующего символа.

Альтернативой pack является использование chr(hex($1)):

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

Если доступно, вы также можете использовать uri_unescape() из URI::Escape:

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

Ответ 4

Это то, что, кажется, работает для меня.

#!/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 'и разрешение эхо-интерпретации\x экранов с использованием опции -e не работает. По какой-то причине команда 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

Исправление: В основном игнорировать 2 символа после% 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; ru

Не уверен, какие осложнения могут возникнуть после обширного тестирования, но работает пока.

Ответ 5

Bash script для этого в исходном 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, просто поместите содержимое файла в качестве аргумента.

Здесь тест, который запустится, если декодированное кодированное содержимое файла отличается (если оно выполняется в течение нескольких секунд, возможно, работает script):

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

Как @barti_ddu в комментариях, \x "должен быть [double-] экранирован".

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

Ответ 7

Если у вас установлен php на вашем сервере, вы можете "cat" или даже "tail" любой файл, с строками с кодировкой url очень легко.

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

Ответ 8

Если вы являетесь разработчиком python, это может быть предпочтительнее

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

urllib является профессионалом при работе с ним

Ответ 9

С GNU awk:

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

Ответ 10

С 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
  • Считываемая встроенная команда считывает стандарт до тех пор, пока не увидит символ линии. Он устанавливает переменную с именем REPLY, равную строке текста, которую она только что прочитала.
  • ${REPLY//%/\\x} заменяет все экземпляры '%' на '\ x'.
  • echo -e интерпретирует \xNN как символ ASCII с шестнадцатеричным значением NN.
  • повторяя этот цикл до тех пор, пока команда чтения не будет выполнена, например. EOF достигнуто.

Это не меняет '+' на ''. Это можно добавить как в гостевой answer. Это использует только BASH и не запускает какой-либо другой процесс, аналогичный гостевому ответу.

Ответ 11

Вот решение, которое выполняется в чистом 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

Ответ 12

$ uenc='H%C3%B6he %C3%BCber%20dem%20Meeresspiegel'
$ utf8=$(echo -e "${uenc//%/\\x}")
$ echo $utf8
Höhe über dem Meeresspiegel
$

Ответ 13

Расширение до fooobar.com/questions/32104/...

для работы с объектами 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

(аргумент должен быть указан)

Ответ 14

Опираясь на аналогичную проблему, моя первоначальная идея заключалась в том, чтобы использовать urldecode из PHP в script, который читал stdin или что-то подобное, но затем я наткнулся на эту идею. Все ответы, похоже, содержат много текста, но не представляют реального решения. Идея звучит, хотя и невероятно проста в работе:

$ mpc | sed -e '1! d'
http://e.org/play.php?name=/Black%20Sun%20Empire%20-%20Sideways%20%28Feat.%20Illy%20Emcee%29

$ basename "$(echo -e `mpc | sed -e '1! d' -e 's/%/\\\\x/g'`)"
Black Sun Empire - Sideways (Feat. Illy Emcee)

Ключом к его выполнению является двойное экранирование \x (об этом уже упоминалось).

Ответ 15

Просто хотел поделиться этим другим решением, чистым bash:

encoded_string="Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B%20en"
printf -v encoded_string "%b" "${encoded_string//\%/\x}"
echo $encoded_string

Ответ 16

Немного измененная версия ответа Python, которая принимает входной и выходной файл в одном лайнере.

cat inputfile.txt | python -c "import sys, urllib as ul; print ul.unquote(sys.stdin.read());" > ouputfile.txt

Ответ 17

$ uenc='H%C3%B6he %C3%BCber%20dem%20Meeresspiegel'
$ utf8=$(printf "${uenc//%/\\x}")
$ echo $utf8
Höhe über dem Meeresspiegel
$