J'essaie d'écrire un script bash à tester qui prend un paramètre et l'envoie via curl au site Web. Je dois encoder la valeur dans l'URL pour m'assurer que les caractères spéciaux sont traités correctement. Quelle est la meilleure façon de procéder?
Voici mon script de base jusqu'à présent:
#!/bin/bash
Host=${1:?'bad Host'}
value=$2
shift
shift
curl -v -d "param=${value}" http://${Host}/somepath $@
Utilisez curl --data-urlencode
; de man curl
:
Ceci publie des données, similaires aux autres options
--data
, à l'exception que cela effectue un codage d'URL. Pour être conforme à CGI, la partie<data>
devrait commencer par un nom suivi d'un séparateur et d'une spécification de contenu.
Exemple d'utilisation:
curl \
--data-urlencode "paramName=value" \
--data-urlencode "secondParam=value" \
http://example.com
Voir la page de manuel pour plus d’informations.
Cela nécessite curl 7.18.0 ou plus récent (publié en janvier 2008) . Utilisez curl -V
pour vérifier votre version.
Voici la réponse pure de BASH.
rawurlencode() {
local string="${1}"
local strlen=${#string}
local encoded=""
local pos c o
for (( pos=0 ; pos<strlen ; pos++ )); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9] ) o="${c}" ;;
* ) printf -v o '%%%02x' "'$c"
esac
encoded+="${o}"
done
echo "${encoded}" # You can either set a return variable (FASTER)
REPLY="${encoded}" #+or echo the result (EASIER)... or both... :p
}
Vous pouvez l'utiliser de deux manières:
easier: echo http://url/q?=$( rawurlencode "$args" )
faster: rawurlencode "$args"; echo http://url/q?${REPLY}
[édité]
Voici la fonction rawurldecode () correspondante, qui - en toute modestie - est géniale.
# Returns a string in which the sequences with percent (%) signs followed by
# two hex digits have been replaced with literal characters.
rawurldecode() {
# This is perhaps a risky gambit, but since all escape characters must be
# encoded, we can replace %NN with \xNN and pass the lot to printf -b, which
# will decode hex for us
printf -v REPLY '%b' "${1//%/\\x}" # You can either set a return variable (FASTER)
echo "${REPLY}" #+or echo the result (EASIER)... or both... :p
}
Avec le jeu de correspondance, nous pouvons maintenant effectuer quelques tests simples:
$ diff rawurlencode.inc.sh \
<( rawurldecode "$( rawurlencode "$( cat rawurlencode.inc.sh )" )" ) \
&& echo Matched
Output: Matched
Et si vous sentez vraiment que vous avez besoin d’un outil externe (enfin, il ira beaucoup plus vite, et pourrait faire des fichiers binaires, etc.), je l’ai trouvé sur mon routeur OpenWRT ...
replace_value=$(echo $replace_value | sed -f /usr/lib/ddns/url_escape.sed)
Où url_escape.sed était un fichier contenant ces règles:
# sed url escaping
s:%:%25:g
s: :%20:g
s:<:%3C:g
s:>:%3E:g
s:#:%23:g
s:{:%7B:g
s:}:%7D:g
s:|:%7C:g
s:\\:%5C:g
s:\^:%5E:g
s:~:%7E:g
s:\[:%5B:g
s:\]:%5D:g
s:`:%60:g
s:;:%3B:g
s:/:%2F:g
s:?:%3F:g
s^:^%3A^g
s:@:%40:g
s:=:%3D:g
s:&:%26:g
s:\$:%24:g
s:\!:%21:g
s:\*:%2A:g
Utilisez le module URI::Escape
de Perl et le uri_escape
dans la deuxième ligne de votre script bash:
...
value="$(Perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
...
Edit: Correction des problèmes de citation, comme suggéré par Chris Johnsen dans les commentaires. Merci!
par souci d'exhaustivité, de nombreuses solutions utilisant sed
ou awk
ne traduisent qu'un ensemble spécial de caractères. Elles sont donc assez volumineuses en fonction de la taille du code et ne traduisent pas non plus d'autres caractères spéciaux à coder.
un moyen sûr d'urlencoder serait de simplement encoder chaque octet, même ceux qui auraient été autorisés.
echo -ne 'some random\nbytes' | xxd -plain | tr -d '\n' | sed 's/\(..\)/%\1/g'
xxd veille à ce que l'entrée soit gérée en octets et non en caractères.
modifier:
xxd est livré avec le paquet vim-common dans Debian et j'étais juste sur un système où il n'était pas installé et je ne voulais pas l'installer. L'option consiste à utiliser hexdump
à partir du paquet bsdmainutils dans Debian. Selon le graphique suivant, bsdmainutils et vim-common devraient avoir une probabilité à peu près égale d'être installés:
mais néanmoins voici une version qui utilise hexdump
à la place de xxd
et permet d'éviter l'appel tr
:
echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'
Une des variantes peut être laide, mais simple:
urlencode() {
local data
if [[ $# != 1 ]]; then
echo "Usage: $0 string-to-urlencode"
return 1
fi
data="$(curl -s -o /dev/null -w %{url_effective} --get --data-urlencode "$1" "")"
if [[ $? != 3 ]]; then
echo "Unexpected error" 1>&2
return 2
fi
echo "${data##/?}"
return 0
}
Voici la version à une ligne par exemple (comme suggéré par Bruno ):
date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-
# If you experience the trailing %0A, use
date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | sed -E 's/..(.*).../\1/'
Je le trouve plus lisible en python:
encoded_value=$(python -c "import urllib; print urllib.quote('''$value''')")
le triple 'garantit que les guillemets simples en valeur ne feront pas mal. urllib est dans la bibliothèque standard. Cela fonctionne pour examiner pour cette URL folle (monde réel):
"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7
Une autre option consiste à utiliser jq
:
jq -sRr @uri
-R
(--raw-input
) traite les lignes d'entrée en tant que chaînes au lieu de les analyser en tant que JSON et -sR
(--Slurp --raw-input
) lit l'entrée en une seule chaîne. -r
(--raw-output
) renvoie le contenu des chaînes au lieu des littéraux JSON.
Si l'entrée ne contient pas de sauts de ligne (ou si vous ne voulez pas les échapper en tant que %0A
), vous pouvez utiliser uniquement jq -Rr @uri
sans l'option -s
.
Ou ce pourcentage encode tous les octets:
xxd -p|tr -d \\n|sed 's/../%&/g'
J'ai trouvé l'extrait suivant utile pour le coller dans une chaîne d'appels de programme, où URI :: Escape n'est peut-être pas installé:
Perl -p -e 's/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg'
( source )
Si vous souhaitez exécuter la requête GET
et utiliser le curl pur, ajoutez simplement --get
à la solution de @ Jacob.
Voici un exemple:
curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed
C'est peut-être le meilleur:
after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
Lien direct vers la version de awk: http://www.shelldorado.com/scripts/cmds/urlencode
Je l'ai utilisé pendant des années et cela fonctionne comme un charme
:
##########################################################################
# Title : urlencode - encode URL data
# Author : Heiner Steven ([email protected])
# Date : 2000-03-15
# Requires : awk
# Categories : File Conversion, WWW, CGI
# SCCS-Id. : @(#) urlencode 1.4 06/10/29
##########################################################################
# Description
# Encode data according to
# RFC 1738: "Uniform Resource Locators (URL)" and
# RFC 1866: "Hypertext Markup Language - 2.0" (HTML)
#
# This encoding is used i.e. for the MIME type
# "application/x-www-form-urlencoded"
#
# Notes
# o The default behaviour is not to encode the line endings. This
# may not be what was intended, because the result will be
# multiple lines of output (which cannot be used in an URL or a
# HTTP "POST" request). If the desired output should be one
# line, use the "-l" option.
#
# o The "-l" option assumes, that the end-of-line is denoted by
# the character LF (ASCII 10). This is not true for Windows or
# Mac systems, where the end of a line is denoted by the two
# characters CR LF (ASCII 13 10).
# We use this for symmetry; data processed in the following way:
# cat | urlencode -l | urldecode -l
# should (and will) result in the original data
#
# o Large lines (or binary files) will break many AWK
# implementations. If you get the message
# awk: record `...' too long
# record number xxx
# consider using GNU AWK (gawk).
#
# o urlencode will always terminate it's output with an EOL
# character
#
# Thanks to Stefan Brozinski for pointing out a bug related to non-standard
# locales.
#
# See also
# urldecode
##########################################################################
PN=`basename "$0"` # Program name
VER='1.4'
: ${AWK=awk}
Usage () {
echo >&2 "$PN - encode URL data, $VER
usage: $PN [-l] [file ...]
-l: encode line endings (result will be one line of output)
The default is to encode each input line on its own."
exit 1
}
Msg () {
for MsgLine
do echo "$PN: $MsgLine" >&2
done
}
Fatal () { Msg "$@"; exit 1; }
set -- `getopt hl "$@" 2>/dev/null` || Usage
[ $# -lt 1 ] && Usage # "getopt" detected an error
EncodeEOL=no
while [ $# -gt 0 ]
do
case "$1" in
-l) EncodeEOL=yes;;
--) shift; break;;
-h) Usage;;
-*) Usage;;
*) break;; # First file name
esac
shift
done
LANG=C export LANG
$AWK '
BEGIN {
# We assume an awk implementation that is just plain dumb.
# We will convert an character to its ASCII value with the
# table ord[], and produce two-digit hexadecimal output
# without the printf("%02X") feature.
EOL = "%0A" # "end of line" string (encoded)
split ("1 2 3 4 5 6 7 8 9 A B C D E F", hextab, " ")
hextab [0] = 0
for ( i=1; i<=255; ++i ) ord [ sprintf ("%c", i) "" ] = i + 0
if ("'"$EncodeEOL"'" == "yes") EncodeEOL = 1; else EncodeEOL = 0
}
{
encoded = ""
for ( i=1; i<=length ($0); ++i ) {
c = substr ($0, i, 1)
if ( c ~ /[a-zA-Z0-9.-]/ ) {
encoded = encoded c # safe character
} else if ( c == " " ) {
encoded = encoded "+" # special handling
} else {
# unsafe character, encode it as a two-digit hex-number
lo = ord [c] % 16
hi = int (ord [c] / 16);
encoded = encoded "%" hextab [hi] hextab [lo]
}
}
if ( EncodeEOL ) {
printf ("%s", encoded EOL)
} else {
print encoded
}
}
END {
#if ( EncodeEOL ) print ""
}
' "$@"
url=$(echo "$1" | sed -e 's/%/%25/g' -e 's/ /%20/g' -e 's/!/%21/g' -e 's/"/%22/g' -e 's/#/%23/g' -e 's/\$/%24/g' -e 's/\&/%26/g' -e 's/'\''/%27/g' -e 's/(/%28/g' -e 's/)/%29/g' -e 's/\*/%2a/g' -e 's/+/%2b/g' -e 's/,/%2c/g' -e 's/-/%2d/g' -e 's/\./%2e/g' -e 's/\//%2f/g' -e 's/:/%3a/g' -e 's/;/%3b/g' -e 's//%3e/g' -e 's/?/%3f/g' -e 's/@/%40/g' -e 's/\[/%5b/g' -e 's/\\/%5c/g' -e 's/\]/%5d/g' -e 's/\^/%5e/g' -e 's/_/%5f/g' -e 's/`/%60/g' -e 's/{/%7b/g' -e 's/|/%7c/g' -e 's/}/%7d/g' -e 's/~/%7e/g')
cela va encoder la chaîne à l'intérieur de $ 1 et la sortir dans $ url. bien que vous n’ayez pas à le mettre dans un var si vous voulez. BTW n'a pas inclus le sed pour l'onglet pensé qu'il allait le transformer en espaces
Voici une solution Bash qui n’appelle aucun programme externe:
uriencode() {
s="${1//'%'/%25}"
s="${s//' '/%20}"
s="${s//'"'/%22}"
s="${s//'#'/%23}"
s="${s//'$'/%24}"
s="${s//'&'/%26}"
s="${s//'+'/%2B}"
s="${s//','/%2C}"
s="${s//'/'/%2F}"
s="${s//':'/%3A}"
s="${s//';'/%3B}"
s="${s//'='/%3D}"
s="${s//'?'/%3F}"
s="${s//'@'/%40}"
s="${s//'['/%5B}"
s="${s//']'/%5D}"
printf %s "$s"
}
Pour ceux d'entre vous qui recherchent une solution qui n'a pas besoin de Perl, voici une solution qui nécessite uniquement hexdump et awk:
url_encode() {
[ $# -lt 1 ] && { return; }
encodedurl="$1";
# make sure hexdump exists, if not, just give back the url
[ ! -x "/usr/bin/hexdump" ] && { return; }
encodedurl=`
echo $encodedurl | hexdump -v -e '1/1 "%02x\t"' -e '1/1 "%_c\n"' |
LANG=C awk '
$1 == "20" { printf("%s", "+"); next } # space becomes plus
$1 ~ /0[adAD]/ { next } # strip newlines
$2 ~ /^[a-zA-Z0-9.*()\/-]$/ { printf("%s", $2); next } # pass through what we can
{ printf("%%%s", $1) } # take hex value of everything else
'`
}
Cousus ensemble à partir de quelques endroits sur le net et de quelques essais et erreurs locaux. Cela fonctionne très bien!
ni2ascii est très pratique:
$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
Vous pouvez émuler le javascript encodeURIComponent
en Perl. Voici la commande:
Perl -pe 's/([^a-zA-Z0-9_.!~*()'\''-])/sprintf("%%%02X", ord($1))/ge'
Vous pouvez définir cela comme un alias bash dans .bash_profile
:
alias encodeURIComponent='Perl -pe '\''s/([^a-zA-Z0-9_.!~*()'\''\'\'''\''-])/sprintf("%%%02X",ord($1))/ge'\'
Vous pouvez maintenant diriger vers encodeURIComponent
:
$ echo -n 'hèllo wôrld!' | encodeURIComponent
h%C3%A8llo%20w%C3%B4rld!
Si vous ne voulez pas dépendre de Perl, vous pouvez également utiliser sed. C'est un peu brouillon, car chaque personnage doit être échappé individuellement. Créez un fichier avec le contenu suivant et appelez-le urlencode.sed
s/%/%25/g
s/ /%20/g
s/ /%09/g
s/!/%21/g
s/"/%22/g
s/#/%23/g
s/\$/%24/g
s/\&/%26/g
s/'\''/%27/g
s/(/%28/g
s/)/%29/g
s/\*/%2a/g
s/+/%2b/g
s/,/%2c/g
s/-/%2d/g
s/\./%2e/g
s/\//%2f/g
s/:/%3a/g
s/;/%3b/g
s//%3e/g
s/?/%3f/g
s/@/%40/g
s/\[/%5b/g
s/\\/%5c/g
s/\]/%5d/g
s/\^/%5e/g
s/_/%5f/g
s/`/%60/g
s/{/%7b/g
s/|/%7c/g
s/}/%7d/g
s/~/%7e/g
s/ /%09/g
Pour l'utiliser, procédez comme suit.
STR1=$(echo "https://www.example.com/change&$ ^this to?%checkthe@-functionality" | cut -d\? -f1)
STR2=$(echo "https://www.example.com/change&$ ^this to?%checkthe@-functionality" | cut -d\? -f2)
OUT2=$(echo "$STR2" | sed -f urlencode.sed)
echo "$STR1?$OUT2"
Cela divisera la chaîne en une partie nécessitant un codage, et la partie correcte, codera la partie qui en a besoin, puis recousue.
Vous pouvez le mettre dans un script sh pour plus de commodité, peut-être qu'il prenne un paramètre à encoder, le place sur votre chemin et vous pouvez simplement appeler:
urlencode https://www.exxample.com?isThisFun=HellNo
Utiliser php à partir d'un script shell:
value="http://www.google.com"
encoded=$(php -r "echo rawurlencode('$value');")
# encoded = "http%3A%2F%2Fwww.google.com"
echo $(php -r "echo rawurldecode('$encoded');")
# returns: "http://www.google.com"
La question porte sur le faire dans bash et il n’est pas nécessaire d'utiliser python ou Perl car il existe en fait une seule commande qui fait exactement ce que vous voulez - "urlencode".
value=$(urlencode "${2}")
Ceci est également bien meilleur, car la réponse Perl ci-dessus, par exemple, ne code pas correctement tous les caractères. Essayez-le avec le tiret long que vous obtenez de Word et vous obtenez le mauvais encodage.
Notez que vous avez besoin de "gridsite-clients" pour fournir cette commande.
Simple PHP option:
echo 'part-that-needs-encoding' | php -R 'echo urlencode($argn);'
Voici la version du noeud:
uriencode() {
node -p "encodeURIComponent('${1//\'/\\\'}')"
}
Une autre approche php:
echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"
Ruby, pour être complet
value="$(Ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "$2")"
Voici ma version pour busybox ash Shell pour un système embarqué, j'avais initialement adopté la variante d'Orwellophile:
urlencode()
{
local S="${1}"
local encoded=""
local ch
local o
for i in $(seq 0 $((${#S} - 1)) )
do
ch=${S:$i:1}
case "${ch}" in
[-_.~a-zA-Z0-9])
o="${ch}"
;;
*)
o=$(printf '%%%02x' "'$ch")
;;
esac
encoded="${encoded}${o}"
done
echo ${encoded}
}
urldecode()
{
# urldecode <string>
local url_encoded="${1//+/ }"
printf '%b' "${url_encoded//%/\\x}"
}
Voici une conversion d'une ligne utilisant Lua, similaire à réponse de blueyed sauf avec tous les caractères non réservés RFC 3986 non codés (comme cette réponse ):
url=$(echo 'print((arg[1]:gsub("([^%w%-%.%_%~])",function(c)return("%%%02X"):format(c:byte())end)))' | lua - "$1")
De plus, vous devrez peut-être vous assurer que les nouvelles lignes de votre chaîne sont converties de LF en CRLF, auquel cas vous pouvez insérer un gsub("\r?\n", "\r\n")
dans la chaîne avant le codage en pourcentage.
Voici une variante qui, dans le style non standard d'application/x-www-form-urlencoded , effectue cette normalisation des nouvelles lignes, ainsi que l'encodage des espaces en tant que '+' au lieu de '% 20' ( qui pourrait probablement être ajouté à l’extrait de code Perl en utilisant une technique similaire).
url=$(echo 'print((arg[1]:gsub("\r?\n", "\r\n"):gsub("([^%w%-%.%_%~ ]))",function(c)return("%%%02X"):format(c:byte())end):gsub(" ","+"))' | lua - "$1")
Voici une fonction POSIX pour le faire:
encodeURIComponent() {
awk 'BEGIN {while (y++ < 125) z[sprintf("%c", y)] = y
while (y = substr(ARGV[1], ++j, 1))
q = y ~ /[[:alnum:]_.!~*\47()-]/ ? q y : q sprintf("%%%02X", z[y])
print q}' "$1"
}
Exemple:
value=$(encodeURIComponent "$2")
Ceci est la version ksh de la réponse de orwellophile contenant les fonctions rawurlencode et rawurldecode (link: Comment urlencoder des données pour une commande curl? ). Je n'ai pas assez de rep pour poster un commentaire, d'où le nouveau post ..
#!/bin/ksh93
function rawurlencode
{
typeset string="${1}"
typeset strlen=${#string}
typeset encoded=""
for (( pos=0 ; pos<strlen ; pos++ )); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9] ) o="${c}" ;;
* ) o=$(printf '%%%02x' "'$c")
esac
encoded+="${o}"
done
print "${encoded}"
}
function rawurldecode
{
printf $(printf '%b' "${1//%/\\x}")
}
print $(rawurlencode "C++") # --> C%2b%2b
print $(rawurldecode "C%2b%2b") # --> C++
Ayant php installé j'utilise cette façon:
URL_ENCODED_DATA=`php -r "echo urlencode('$DATA');"`
Qu'est-ce qui analyserait les URL mieux que javascript?
node -p "encodeURIComponent('$url')"
Ce qui suit est basé sur la réponse d'Orwellophile, mais résout le bogue multi-octets mentionné dans les commentaires en définissant LC_ALL = C (une astuce de vte.sh). Je l'ai écrit sous forme de fonction convenable Prompt_COMMAND, car c'est ainsi que je l'utilise.
print_path_url() {
local LC_ALL=C
local string="$PWD"
local strlen=${#string}
local encoded=""
local pos c o
for (( pos=0 ; pos<strlen ; pos++ )); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9/] ) o="${c}" ;;
* ) printf -v o '%%%02x' "'$c"
esac
encoded+="${o}"
done
printf "\033]7;file://%s%s\007" "${HOSTNAME:-}" "${encoded}"
}