Como urlencode dados para o comando curl?


319

Estou tentando escrever um script bash para teste que usa um parâmetro e o envia através de curl para o site. Preciso codificar o valor do URL para garantir que os caracteres especiais sejam processados ​​corretamente. Qual é a melhor maneira de fazer isso?

Aqui está o meu script básico até agora:

#!/bin/bash
host=${1:?'bad host'}
value=$2
shift
shift
curl -v -d "param=${value}" http://${host}/somepath $@

Consulte também: Como decodificar uma string codificada em URL no shell? para soluções sem enrolamento.
kenorb

Respostas:


396

Use curl --data-urlencode; de man curl:

Isso publica dados, semelhantes às outras --dataopções, com a exceção de que ela executa codificação de URL. Para ser compatível com CGI, a <data>parte deve começar com um nome seguido por um separador e uma especificação de conteúdo.

Exemplo de uso:

curl \
    --data-urlencode "paramName=value" \
    --data-urlencode "secondParam=value" \
    http://example.com

Veja a página de manual para mais informações.

Isso requer a curvatura 7.18.0 ou mais recente (lançada em janeiro de 2008) . Use curl -Vpara verificar qual versão você possui.

Você também pode codificar a string de consulta :

curl -G \
    --data-urlencode "p1=value 1" \
    --data-urlencode "p2=value 2" \
    http://example.com
    # http://example.com?p1=value%201&p2=value%202

5
Parece funcionar apenas para http POST. Documentação aqui: curl.haxx.se/docs/manpage.html#--data-urlencode
Stan James

82
@StanJames Se você usá-lo assim, o curl também pode fazer a codificação para uma solicitação GET. curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
Kart

13
@ kberg, na verdade, isso funcionará apenas para dados de consulta. curl acrescentará um '?' seguido pelos parâmetros codificados em url. Se você quiser codificar algum postfix de URL (como um CouchDB GET para algum ID de documento), '--data-urlencode' não funcionará.
Bokeh

1
Não funciona para curl --data-urlencode "description=![image]($url)" www.example.com. Alguma idéia do porquê? `
Khurshid Alam

1
@NadavB Escaping "the‽
BlackJack

179

Aqui está a resposta BASH pura.

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
}

Você pode usá-lo de duas maneiras:

easier:  echo http://url/q?=$( rawurlencode "$args" )
faster:  rawurlencode "$args"; echo http://url/q?${REPLY}

[editado]

Aqui está a função rawurldecode () correspondente, que - com toda a modéstia - é incrível.

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

Com o conjunto de correspondência, agora podemos executar alguns testes simples:

$ diff rawurlencode.inc.sh \
        <( rawurldecode "$( rawurlencode "$( cat rawurlencode.inc.sh )" )" ) \
        && echo Matched

Output: Matched

E se você realmente acha que precisa de uma ferramenta externa (bem, ela será muito mais rápida e poderá fazer arquivos binários e coisas assim ...) Encontrei isso no meu roteador OpenWRT ...

replace_value=$(echo $replace_value | sed -f /usr/lib/ddns/url_escape.sed)

Onde url_escape.sed era um arquivo que continha estas regras:

# 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

4
Infelizmente, esse script falha em alguns caracteres, como 'é' e '½', produzindo 'e% FFFFFFFFFFFFFFCC' e '% FFFFFFFFFFFFFFFFC2', respectivamente (b / c do loop por caractere, acredito).
Matthemattics

1
Ele não funciona para mim no Bash 4.3.11 (1). A string Jogging «à l'Hèze»gera Jogging%20%abà%20l%27Hèze%bbque não pode ser alimentada por JS decodeURIComponent:(
dmcontador

2
Nesse primeiro bloco de código, o que significa o último parâmetro para printf? Ou seja, por que são aspas duplas, aspas simples, cifrão, letra c, aspas duplas? A aspas simples faz?
Colin Fraizer

1
@dmcontador - é apenas um script humilde do bash, não tem concepção de caracteres de vários bytes ou unicode. Quando \u0144vir um caractere como ń ( ), ele produzirá% 144, ╡ ( \u2561) será% 2561. As respostas corretas codificadas para essas matérias-primas seriam% C5% 84% 0A e% E2% 95% A1, respectivamente.
Orwellophile

1
@ColinFraizer, as aspas simples servem para converter o seguinte caractere em seu valor numérico. ref. pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Sam

94

Use o URI::Escapemódulo e a uri_escapefunção do Perl na segunda linha do seu script bash:

...

value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
...

Editar: Corrija os problemas de citação, conforme sugerido por Chris Johnsen nos comentários. Obrigado!


2
URI :: Escape pode não estar instalado, verifique minha resposta nesse caso.
blueyed

Corrigi isso (use echo, pipe e <>), e agora funciona mesmo quando $ 2 contém um apóstrofo ou aspas duplas. Obrigado!
Dubek

9
Você também elimina echo:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen

1
A versão de Chris Johnsen é melhor. Eu tinha $ {True} na minha expressão de teste e, usando isso via echo, disparou a expansão da variável uri_escape / Perl.
precisa saber é o seguinte

1
@ jrw32982 sim, olhando para trás, é bom ter outro idioma para realizar essa tarefa. Se eu pudesse, eu iria tirar o meu downvote, mas infelizmente ele estiver bloqueado no.
thecoshman

69

Outra opção é usar jq(como um filtro):

jq -sRr @uri

-R( --raw-input) trata as linhas de entrada como seqüências de caracteres em vez de analisá-las como JSON e -sR( --slurp --raw-input) lê a entrada em uma única sequência. -r(--raw-output ) gera o conteúdo das strings em vez dos literais JSON.

Se a entrada não for a saída de outro comando, você poderá armazená-la em uma jqvariável de cadeia:

jq -nr --arg v "my shell string" '$v|@uri'

-n( --null-input) não lê entrada e --arg name valuearmazena valuena variável namecomo uma sequência. No filtro, $name(entre aspas simples, para evitar a expansão pelo shell), referencia a variávelname .

Embrulhado como uma função Bash, isso se torna:

function uriencode { jq -nr --arg v "$1" '$v|@uri'; }

Ou esse percentual codifica todos os bytes:

xxd -p|tr -d \\n|sed 's/../%&/g'

3
<3 ... deve ser o melhor e aceito IMO (sim, se você puder dizer curlpara codificar que funciona e se o bash tiver um built-in que seria aceitável - mas jqparece ser o ajuste certo, estou longe de atingir o nível de conforto com esta ferramenta)
encerrada 16/11

5
para quem quer saber a mesma coisa que eu: @urinão é alguma variável, mas um filtro jq literal usado para formatar seqüências de caracteres e escapar; veja jq o manual para mais detalhes (desculpe, nenhuma ligação direta, é necessário procurar @urina página ...)
SSC

a versão xxd é exatamente o tipo de coisa que eu estava procurando. Mesmo que esteja um pouco sujo, é curto e não depende
Rian Sanderson 21/11

1
Um uso amostra de jq a url-codificação:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal

67

por uma questão de integridade, muitas soluções usando sedouawk traduzem apenas um conjunto especial de caracteres e, portanto, são muito grandes em tamanho de código e também não traduzem outros caracteres especiais que devem ser codificados.

uma maneira segura de codificar por URL seria codificar apenas cada byte - mesmo aqueles que seriam permitidos.

echo -ne 'some random\nbytes' | xxd -plain | tr -d '\n' | sed 's/\(..\)/%\1/g'

O xxd está cuidando aqui para que a entrada seja tratada como bytes e não como caracteres.

editar:

O xxd vem com o pacote vim-common no Debian e eu estava em um sistema em que não estava instalado e não queria instalá-lo. A alternativa é usarhexdump o pacote bsdmainutils no Debian. De acordo com o gráfico a seguir, bsdmainutils e vim-common devem ter uma probabilidade quase igual de instalação:

http://qa.debian.org/popcon-png.php?packages=vim-common%2Cbsdmainutils&show_installed=1&want_legend=1&want_ticks=1

mas, no entanto, aqui uma versão que usa em hexdumpvez de xxde permite evitar a trchamada:

echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'

1
xxd -plaindeve acontecer DEPOIS tr -d '\n'!
Qdii 08/07

3
@qdii why? isso não apenas tornaria impossível urlencodar novas linhas, mas também inseriria incorretamente novas linhas criadas por xxd na saída.
Josch

1
@josch. Isto é simplesmente errado. Primeiro, qualquer \ncaractere será traduzido por xxd -plainpara 0a. Não tome minha palavra para ela, tente você mesmo: echo -n -e '\n' | xxd -plainIsto prova que o seu tr -d '\n'é inútil aqui como não pode haver qualquer \ndepois xxd -plain Segundo, echo foobaradiciona seu próprio \npersonagem no final da cadeia de caracteres, por isso xxd -plainnão é alimentado com foobarcomo esperado, mas com foobar\n. então o xxd -plain converte em alguma sequência de caracteres que termina 0a, tornando-o inadequado para o usuário. Você pode adicionar -na echoresolvê-lo.
Qdii

6
@qdii de fato -n estava faltando para eco, mas a xxdchamada pertence à frente da tr -dchamada. Ele pertence a ele para que qualquer nova linha foobarseja traduzida por xxd. A tr -dseguir à xxdchamada é para remover as novas linhas que o xxd produz. Parece que você nunca tem foobar por tempo suficiente, o que xxdproduz novas linhas, mas, para entradas longas, ele o fará. Então o tr -dé necessário. Em contraste com sua suposição, tr -dNÃO foi para remover novas linhas da entrada, mas da xxdsaída. Eu quero manter as novas linhas na entrada. Seu único ponto válido é que esse eco adiciona uma nova linha desnecessária.
Josch

1
@qdii e sem ofensa - Eu só acho que você está errado, exceto para o echo -nque eu estava realmente faltando
Josch

62

Uma das variantes, pode ser feia, mas simples:

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
}

Aqui está a versão one-liner, por exemplo (como sugerido por 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/'

1
Eu acho que essa é uma maneira muito inteligente de reutilizar a codificação de URL do cURL.
Solidsnack 24/10/12

13
Isso é absolutamente brilhante! Eu realmente gostaria que você tivesse deixado uma linha para que as pessoas possam ver como é realmente simples. Para URL codificar o resultado do datecomando ... date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(Você tem que cutos 2 primeiros caracteres fora, porque a saída de onda é um tecnicamente um URL relativo com uma string de consulta.)
de Bruno Bronosky

2
@BrunoBronosky Sua variante de uma linha é boa, mas aparentemente adiciona um "% 0A" ao final da codificação. Usuários cuidado. A versão da função parece não ter esse problema.
precisa saber é o seguinte

7
Para evitar %0Ano final, use em printfvez de echo.
Kenorb

2
o forro é fantástico
Stephen Blum

49

Acho mais legível em python:

encoded_value=$(python -c "import urllib; print urllib.quote('''$value''')")

o triplo 'garante que aspas simples em valor não sejam prejudiciais. O urllib está na biblioteca padrão. Ele funciona, por exemplo, para este URL louco (do mundo real):

"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7

2
Eu tive alguns problemas com aspas e caracteres especiais com a citação tripla, isso parecia funcionar basicamente para tudo: encoded_value = "$ (echo -n" $ {data} "| python -c" import urllib; import sys; sys.stdout. write (urllib.quote (sys.stdin.read ())) ")";
Pare de caluniar Monica Cellio

Versão do Python 3 seria encoded_value=$(python3 -c "import urllib.parse; print (urllib.parse.quote('''$value'''))").
Creshal

1
python -c 'import urllib, sys; sys.stdout.writelines(urllib.quote_plus(l, safe="/\n") for l in sys.stdin)'tem quase nenhum citando problemas, e deve haver memória / velocidade eficiente (não tenho verificado, para salvar estrabismo)
Alois Mahdal

2
Seria muito mais seguro se referir ao sys.argvinvés de substituir $valuepor uma string posteriormente analisada como código. E se valuecontido ''' + __import__("os").system("rm -rf ~") + '''?
Charles Duffy

2
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
Rockallite

30

Eu achei o seguinte trecho útil para colá-lo em uma cadeia de chamadas de programa, em que o URI :: Escape pode não estar instalado:

perl -p -e 's/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg'

( fonte )


4
trabalhou para mim. Eu mudei para perl -lpe ... (a letra ell). Isso removeu a nova linha à direita, necessária para meus propósitos.
JohnnyLambada

2
Para sua informação, para fazer o inverso disso, use perl -pe 's/\%(\w\w)/chr hex $1/ge'(fonte: unix.stackexchange.com/questions/159253/… )
Sridhar Sarnobat 10/15/15

2
Dependendo especificamente dos caracteres que você precisa codificar, você pode simplificar isso para perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'permitir letras, números e sublinhados, mas codifica todo o resto.
robru

23

Se você deseja executar a GETsolicitação e usar o curl puro, adicione --geta solução do @ Jacob.

Aqui está um exemplo:

curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed

15

Link direto para a versão do awk: http://www.shelldorado.com/scripts/cmds/urlencode
Eu usei por anos e funciona como um encanto

:
##########################################################################
# Title      :  urlencode - encode URL data
# Author     :  Heiner Steven (heiner.steven@odn.de)
# 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 ""
    }
' "$@"

Existe uma variação simples para obter codificação UTF-8 em vez de ASCII?
Avgvstvs 02/10/2015

15

Este pode ser o melhor:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")

Isso funciona para mim com duas adições: 1. substitua -e por -n para evitar adicionar uma nova linha ao final do argumento e 2. adicione '%%' à string printf para colocar um% na frente de cada par de dígitos hexadecimais.
Rob Fagen

funciona depois de adicionar $ colchete à frente after=$(echo -e ...
Roman Rhrn Nesterov 01/09/16

1
Por favor, explique como isso funciona. O odcomando não é comum.
Mark Stosberg 19/11/19

Isso não funciona com o OS X odporque usa um formato de saída diferente do GNU od. Por exemplo, printf aa|od -An -tx1 -v|tr \ -imprime -----------61--61--------------------------------------------------------com o OS X ode -61-61com o GNU od. Você pode usar od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\ncom o OS X odou o GNU od. xxd -p|sed 's/../%&/g'|tr -d \\nfaz a mesma coisa, mesmo que xxdnão esteja no POSIX, mas odestá.
Nisetama

2
Embora este trabalho poder, ele escapa cada personagem
Charlie

11

Aqui está uma solução Bash que não chama nenhum programa externo:

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

4
Isso se comporta de maneira diferente entre as versões do bash. No RHEL 6.9, o bash é 4.1.2 e inclui aspas simples. Enquanto o Debian 9 e o bash 4.4.12 são bons com aspas simples. Para mim, remover as aspas simples funcionava em ambos. s = "$ {s // ',' /% 2C}"
muni764

1
Atualizei a resposta para refletir sua descoberta, @ muni764.
Davidchambers

Apenas um aviso ... isso não codifica coisas como o personagemá
diogovk 27/04

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

isso codificará a string dentro de $ 1 e a produzirá em $ url. embora você não precise colocá-lo em um var, se quiser. BTW não incluiu o sed para tab pensei que iria transformá-lo em espaços


5
Sinto que essa não é a maneira recomendada de fazer isso.
Cody Gray

2
explique seu sentimento, por favor ... porque eu o que afirmei funciona e o usei em vários scripts, então eu sei que funciona para todos os caracteres que listei. por isso, explique por que alguém não usaria meu código nem o perl, pois o título é "URLEncode from a bash script" e não o perl.
manoflinux

às vezes nenhuma solução pérola é necessário que esta pode vir a calhar
Yuval Rimar

3
Essa não é a maneira recomendada de fazer isso, porque a lista negra é uma prática ruim e, de qualquer maneira, é unicode hostil.
Ekevoo

Esta foi a solução mais amigável compatível com o
arquivo cat.txt


7

Para aqueles que procuram uma solução que não precisa de perl, aqui está uma que precisa apenas de hexdump e 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
   '`
}

Costurados em vários lugares da rede e algumas tentativas e erros locais. Funciona muito bem!


7

O uni2ascii é muito útil:

$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C

2
Isso não funciona para caracteres dentro do intervalo ASCII, essa necessidade citando, como %e no espaço (essa última pode ser remediado com a -sbandeira)
Boldewyn

7

Se você não quer depender do Perl, também pode usar o sed. É um pouco confuso, pois cada personagem precisa ser escapado individualmente. Crie um arquivo com o seguinte conteúdo e chame-ourlencode.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

Para usá-lo, faça o seguinte.

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"

Isso dividirá a sequência em uma parte que precisa de codificação, e a parte que é boa, codifica a parte que precisa dela e, em seguida, é costurada novamente.

Você pode colocar isso em um script sh por conveniência, talvez precise de um parâmetro para codificar, coloque-o no seu caminho e, em seguida, basta chamar:

urlencode https://www.exxample.com?isThisFun=HellNo

fonte


7

Você pode emular javascript encodeURIComponentem perl. Aqui está o comando:

perl -pe 's/([^a-zA-Z0-9_.!~*()'\''-])/sprintf("%%%02X", ord($1))/ge'

Você pode definir isso como um alias do bash em .bash_profile:

alias encodeURIComponent='perl -pe '\''s/([^a-zA-Z0-9_.!~*()'\''\'\'''\''-])/sprintf("%%%02X",ord($1))/ge'\'

Agora você pode canalizar para encodeURIComponent:

$ echo -n 'hèllo wôrld!' | encodeURIComponent
h%C3%A8llo%20w%C3%B4rld!

6

Aqui está a versão do nó:

uriencode() {
  node -p "encodeURIComponent('${1//\'/\\\'}')"
}

1
Isso não será interrompido se houver outros caracteres na cadeia que não sejam válidos entre aspas simples, como uma barra invertida ou novas linhas?
Stuart P. Bentley

Bom ponto. Se quisermos escapar de todos os caracteres problemáticos do Bash, podemos executar as substituições diretamente e evitar nodecompletamente. Publiquei uma solução apenas para Bash. :)
davidchambers

1
Esta variante encontrada em outro lugar na página evita o problema citando lendo o valor de STDIN:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Mark Stosberg

6

A questão é fazer isso no bash e não há necessidade de python ou perl, pois na verdade existe um único comando que faz exatamente o que você deseja - "urlencode".

value=$(urlencode "${2}")

Isso também é muito melhor, pois a resposta perl acima, por exemplo, não codifica todos os caracteres corretamente. Experimente com o longo traço obtido do Word e a codificação incorreta.

Observe que você precisa "gridsite-clients" instalado para fornecer este comando.


1
Minha versão do bash (GNU 3.2) não possui urlencode. Qual versão você está usando?
Sridhar Sarnobat

1
Eu tenho 4.3.42, mas o comando urlencode é fornecido por "gridsite-clients". Tente instalar isso e você deve ficar bem.
Dylan

5
Portanto, sua resposta não é melhor do que qualquer que requerem outras coisas instalados (Python, Perl, lua, ...)
Cyrille Pontvieux

Só que requer apenas a instalação de um único utilitário em vez de um idioma (e bibliotecas) inteiro, além de ser super simples e claro para ver o que está fazendo.
Dylan

Um link primeiro para a página do pacote / projeto que fornece esse comando teria sido útil.
Doron Behar 27/03

6

Opção PHP simples:

echo 'part-that-needs-encoding' | php -R 'echo urlencode($argn);'

4

Ruby, para completar

value="$(ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "$2")"

4

Outra abordagem php:

echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"

2
echoacrescentará um caractere de nova linha (hex 0xa). Para impedir isso, use echo -n.
Mathew Hall

3

Aqui está a minha versão do busybox ash shell para um sistema incorporado. Adotei originalmente a variante do 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}"
}

2

Aqui está uma função POSIX para fazer isso:

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

Exemplo:

value=$(encodeURIComponent "$2")

Fonte


2

Aqui está uma conversão de uma linha usando Lua, semelhante à resposta do blueyed, exceto com todos os caracteres não reservados da RFC 3986 deixados sem codificação (como esta resposta ):

url=$(echo 'print((arg[1]:gsub("([^%w%-%.%_%~])",function(c)return("%%%02X"):format(c:byte())end)))' | lua - "$1")

Além disso, pode ser necessário garantir que as novas linhas da sua sequência sejam convertidas de LF para CRLF. Nesse caso, você pode inserir um gsub("\r?\n", "\r\n") na cadeia antes da codificação percentual.

Aqui está uma variante que, no estilo não padrão de application / x-www-form-urlencoded , faz essa normalização de nova linha, além de codificar espaços como '+' em vez de '% 20' (que provavelmente poderiam ser adicionados ao arquivo Snippet Perl usando uma técnica semelhante).

url=$(echo 'print((arg[1]:gsub("\r?\n", "\r\n"):gsub("([^%w%-%.%_%~ ]))",function(c)return("%%%02X"):format(c:byte())end):gsub(" ","+"))' | lua - "$1")

1

Tendo o php instalado, eu uso desta maneira:

URL_ENCODED_DATA=`php -r "echo urlencode('$DATA');"`

1

Esta é a versão ksh da resposta do orwellophile que contém as funções rawurlencode e rawurldecode (link: Como urlencode dados para o comando curl? ). Não tenho representante suficiente para postar um comentário, daí o novo 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++

1

O que analisaria URLs melhor que o javascript?

node -p "encodeURIComponent('$url')"

Fora do escopo da pergunta operacional. Não bash, não enrolar. Mesmo que eu tenha certeza que funciona muito bem se o nó estiver disponível.
Cyrille Pontvieux

Por que fazer o down-vote desta e não a resposta do python / perl? Além disso, como isso não responde à pergunta original "Como urlencode dados para o comando curl?". Isso pode ser usado em um script bash e o resultado pode ser fornecido em um comando curl.
Nestor Urquiza

Também votei contra os outros. A questão era como fazer isso em um script bash. Se outra linguagem for usada como node / js, python ou perl, não será necessário usar o curl diretamente.
Cyrille Pontvieux 03/08/19

2
Embora eu não tenha me dado ao trabalho de votar, o problema com este comando é que ele requer que os dados sejam escapados corretamente para uso em javascript. Como tentar com aspas simples e alguma loucura de barra invertida. Se você quiser usar nó, você coisas melhor leitura do stdin comonode -p 'encodeURIComponent(require("fs").readFileSync(0))'
Michael Krelin - hacker de

1
Cuidado com a solução do @ MichaelKrelin-hacker, se você estiver direcionando dados do STDIN, certifique-se de não incluir uma nova linha à direita. Por exemplo, echo | ...está errado, enquanto echo -n | ...suprime a nova linha.
Mark Stosberg

0

O seguinte é baseado na resposta do Orwellophile, mas resolve o bug multibyte mencionado nos comentários, definindo LC_ALL = C (um truque do vte.sh). Eu escrevi na forma de função adequada PROMPT_COMMAND, porque é assim que eu a uso.

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}"
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.