bash - Cómo decodificar cadena codificada en URL en shell?
awk sed (17)
Aquí hay una solución que se hace en pure bash donde input y output son variables bash. Descodificará ''+'' como un espacio y manejará el espacio ''% 20'', así como otros caracteres codificados en%.
#!/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
Tengo un archivo con una lista de usuarios-agentes que están codificados. P.ej:
Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B%20en
Quiero un script de shell que pueda leer este archivo y escribir en un nuevo archivo con cadenas decodificadas.
Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en
He intentado utilizar este ejemplo para ponerlo en funcionamiento, pero hasta ahora no ha funcionado.
$ echo -e "$(echo "%31+%32%0A%33+%34" | sed ''y/+/ /; s/%///x/g'')"
Mi script se ve así:
#!/bin/bash
for f in *.log; do
echo -e "$(cat $f | sed ''y/+/ /; s/%//x/g'')" > y.log
done
Aquí hay una solución simple de una línea.
$ urldecode() { : "${*//+/ }"; echo -e "${_//%///x}"; }
Puede parecer perl :) pero es pura bash. No awks, no seds ... no overheads. Usando el: built-in, parámetros especiales, sustitución de patrones y la opción -e incorporada del eco para traducir códigos hexadecimales en caracteres. Ver la página de manual de bash para más detalles. Puede usar esta función como comando separado
$ urldecode https%3A%2F%2Fgoogle.com%2Fsearch%3Fq%3Durldecode%2Bbash
https://google.com/search?q=urldecode+bash
o en asignaciones variables, así:
$ x="http%3A%2F%2F.com%2Fsearch%3Fq%3Durldecode%2Bbash"
$ y=$(urldecode "$x")
$ echo "$y"
http://.com/search?q=urldecode+bash
Awk de GNU
#!/usr/bin/awk -fn
@include "ord"
BEGIN {
RS = "%.."
}
{
printf RT ? $0 chr("0x" substr(RT, 2)) : $0
}
O
#!/bin/sh
awk -niord ''{printf RT?$0chr("0x"substr(RT,2)):$0}'' RS=%..
Uso de awk printf para urldecode text
Como dijo barti_ddu en los comentarios, /x
"debería ser [doble]" escapado ".
% 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
En lugar de mezclar Bash y sed, haría todo esto en Python. Aquí hay un corte aproximado de cómo:
#!/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'')
Con BASH, para leer el porcentaje de URL codificada de estándar en y decodificar:
while read; do echo -e ${REPLY//%///x}; done
Presione CTRL - D para señalar el final del archivo (EOF) y salga con elegancia.
Puede decodificar el contenido de un archivo configurando el archivo para que sea estándar en:
while read; do echo -e ${REPLY//%///x}; done < file
También puede decodificar la entrada desde un conducto, por ejemplo:
echo ''a%21b'' | while read; do echo -e ${REPLY//%///x}; done
- El comando de lectura integrada lee estándar hasta que ve un carácter de alimentación de línea. Establece una variable llamada
REPLY
igual a la línea de texto que acaba de leer. -
${REPLY//%///x}
reemplaza todas las instancias de ''%'' con ''/ x''. -
echo -e
interpreta/xNN
como el carácter ASCII con valor hexadecimal deNN
. - mientras repite este bucle hasta que falla el comando de lectura, ej. EOF ha sido alcanzado.
Esto no cambia ''+'' a ''''. Eso se puede agregar como en la answer del invitado. Esto solo usa BASH y no inicia ningún otro proceso, similar a la respuesta del invitado.
Con GNU awk
:
gawk -vRS=''%[0-9a-fA-F]{2}'' ''RT{sub("%","0x",RT);RT=sprintf("%c",strtonum(RT))}
{gsub(//+/," ");printf "%s", $0 RT}''
Enfrentando un problema similar, mi idea inicial era usar urldecode de PHP en un script que lee stdin o algo así, pero luego me encontré con esta idea. Todas las respuestas parecen tener mucho texto, pero no presentan una solución real. La idea es sólida e increíblemente fácil de usar:
$ 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)
La clave para hacer que funcione es el doble escape / x (esto ya se ha mencionado).
Esto es lo que parece estar funcionando para mí.
#!/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
Reemplazar ''+'' s con espacios, y% signos con ''/ x'' escapes, y dejar que echo interprete los / x escapes usando la opción ''-e'' no funcionaba. Por alguna razón, el comando cat estaba imprimiendo el signo% como su propia forma codificada% 25. Entonces sed simplemente estaba reemplazando% 25 con / x25. Cuando se usaba la opción -e, simplemente evaluaba / x25 como% y el resultado era el mismo que el original.
Rastro:
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
Solución: Básicamente ignore los 2 caracteres después del% en 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;
No estoy seguro de qué complicaciones resultaría, después de pruebas extensas, pero funciona por ahora.
Expandiendo a https://.com/a/37840948/8142470
para trabajar con entidades HTML
$ htmldecode () {: "$ {* // + /}"; echo -e "$ {_ // & # x / / x}" | tr -d '';''; }
$ htmldecode "http & # x3A; & # x2F; & # x2F; google.com & # x2F; búsqueda && # x3F; q & # x3D; urldecode & # x2B; bash" http://google.com/search&?q=urldecode+bash
(el argumento debe ser citado)
Script Bash para hacerlo en nativo Bash ( fuente original ):
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}"
}
Si desea urldecode contenido de archivo, simplemente ponga el contenido del archivo como argumento.
Aquí hay una prueba que se detendrá si el contenido codificado del archivo decodificado difiere (si se ejecuta durante unos segundos, la secuencia de comandos probablemente funcione correctamente):
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
Si tiene instalado php en su servidor, puede "cat" o incluso "rastrear" cualquier archivo, con cadenas codificadas en url muy fácilmente.
tail -f nginx.access.log | php -R ''echo urldecode($argn)."/n";''
Solo quería compartir esta otra solución, pure 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
Una versión ligeramente modificada de la respuesta de Python que acepta un archivo de entrada y salida en un solo liner.
cat inputfile.txt | python -c "import sys, urllib as ul; print ul.unquote(sys.stdin.read());" > ouputfile.txt
si eres un desarrollador de Python , esto quizás prefiera
echo "%21%20" | python -c "import sys, urllib as ul; print ul.unquote(sys.stdin.read());"
urllib es profesional en manejarlo
perl -pi.back -e ''y/+/ /;s/%([/da-f]{2})/pack H2,$1/gie'' ./*.log
Con -i
actualiza los archivos in situ (algunas implementaciones sed
han tomado prestado de perl
) con .back
como la extensión de copia de seguridad.
s/x/y/e
sustituye x
con la valoración e del código y
perl.
El código de perl en este caso usa un pack
para empaquetar el número hexadecimal capturado en $1
(el primer par de paréntesis en la expresión regular) como el personaje correspondiente.
Una alternativa al pack
es usar chr(hex($1))
:
perl -pi.back -e ''y/+/ /;s/%([/da-f]{2})/chr hex $1/gie'' ./*.log
Si está disponible, también puede usar uri_unescape()
desde URI::Escape
:
perl -pi.back -MURI::Escape -e ''y/+/ /;$_=uri_unescape$_'' ./*.log
$ uenc=''H%C3%B6he %C3%BCber%20dem%20Meeresspiegel''
$ utf8=$(echo -e "${uenc//%///x}")
$ echo $utf8
Höhe über dem Meeresspiegel
$
$ uenc=''H%C3%B6he %C3%BCber%20dem%20Meeresspiegel''
$ utf8=$(printf "${uenc//%///x}")
$ echo $utf8
Höhe über dem Meeresspiegel
$