Ответ 1
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=%..
У меня есть файл со списком пользовательских агентов, которые закодированы. Например:.
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
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=%..
Вот простое однострочное решение.
$ 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
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
Это то, что, кажется, работает для меня.
#!/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
Не уверен, какие осложнения могут возникнуть после обширного тестирования, но работает пока.
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
Как @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')
Если у вас установлен php на вашем сервере, вы можете "cat" или даже "tail" любой файл, с строками с кодировкой url очень легко.
tail -f nginx.access.log | php -R 'echo urldecode($argn)."\n";'
Если вы являетесь разработчиком python, это может быть предпочтительнее
echo "%21%20" | python -c "import sys, urllib as ul; print ul.unquote(sys.stdin.read());"
urllib является профессионалом при работе с ним
С GNU awk
:
gawk -vRS='%[0-9a-fA-F]{2}' 'RT{sub("%","0x",RT);RT=sprintf("%c",strtonum(RT))}
{gsub(/\+/," ");printf "%s", $0 RT}'
С 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
.Это не меняет '+' на ''. Это можно добавить как в гостевой answer. Это использует только BASH и не запускает какой-либо другой процесс, аналогичный гостевому ответу.
Вот решение, которое выполняется в чистом 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
$ uenc='H%C3%B6he %C3%BCber%20dem%20Meeresspiegel'
$ utf8=$(echo -e "${uenc//%/\\x}")
$ echo $utf8
Höhe über dem Meeresspiegel
$
Расширение до
fooobar.com/questions/32104/...
для работы с объектами HTML
$htmldecode() {: "$ {*//+/}"; echo -e "$ {_//& # x/\ x}" | tr -d ';'; }
$htmldecode" http://google.com/search&?q=urldecode+bash " http://google.com/search&?q=urldecode+bash
(аргумент должен быть указан)
Опираясь на аналогичную проблему, моя первоначальная идея заключалась в том, чтобы использовать 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 (об этом уже упоминалось).
Просто хотел поделиться этим другим решением, чистым 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
Немного измененная версия ответа Python, которая принимает входной и выходной файл в одном лайнере.
cat inputfile.txt | python -c "import sys, urllib as ul; print ul.unquote(sys.stdin.read());" > ouputfile.txt
$ uenc='H%C3%B6he %C3%BCber%20dem%20Meeresspiegel'
$ utf8=$(printf "${uenc//%/\\x}")
$ echo $utf8
Höhe über dem Meeresspiegel
$