432

パラメータを受け取り、curl を介して Web サイトに送信するテスト用の bash スクリプトを作成しようとしています。特殊文字が適切に処理されるように、値を URL エンコードする必要があります。これを行う最善の方法は何ですか?

これまでの私の基本的なスクリプトは次のとおりです。

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

36 に答える 36

526

使用しcurl --data-urlencodeます。からman curl:

--dataこれは、URL エンコードを実行することを除いて、他のオプションと同様にデータを送信します。CGI に準拠するには、<data>パーツは名前で始まり、その後に区切り記号とコンテンツ仕様が続く必要があります。

使用例:

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

詳細については、man ページを参照してください。

これには、curl 7.18.0 以降 (2008 年 1 月リリース)が必要です。お使い curl -Vのバージョンを確認するために使用します。

クエリ文字列をエンコードすることもできます:

curl -G \
    --data-urlencode "p1=value 1" \
    --data-urlencode "p2=value 2" \
    http://example.com
    # http://example.com?p1=value%201&p2=value%202
于 2010-01-08T13:05:40.657 に答える
205

これが純粋なBASHの答えです。

更新: 多くの変更が議論されているため、これをhttps://github.com/sfinktah/bash/blob/master/rawurlencode.inc.shに配置して、誰でも PR を発行できるようにしました。

: このソリューションは、Unicode またはマルチバイト文字をエンコードすることを意図したものではありません。これは、BASH の控えめなネイティブ機能の範囲外です。'&'、'=' などのように、POST または GET リクエストで引数の受け渡しを台無しにするシンボルをエンコードすることのみを目的としています。

非常に重要な注意: どの言語でも、独自の Unicode 変換関数を記述しようとしないでください。回答の最後を参照してください。

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
}

次の 2 つの方法で使用できます。

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

[編集]

ここに対応する rawurldecode() 関数があります。控えめに言っても、これはすばらしいものです。

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

マッチング セットを使用して、いくつかの簡単なテストを実行できます。

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

Output: Matched

そして、あなたが本当に外部ツールが必要だと感じているなら (まあ、それははるかに速くなり、バイナリファイルなどを実行するかもしれません...) 私は私の OpenWRT ルーターでこれを見つけました...

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

url_escape.sed は、次のルールを含むファイルでした。

# 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

xxdUTF-8 入力を処理できる BASH (おそらく非常に長いルールセットを使用) でそのようなスクリプトを作成することは不可能ではありませんが、より高速で信頼性の高い方法があります。UTF-8 を UTF-32 にデコードしようとすることは、正確に行うのは簡単なことではありませんが、不正確に行うのは非常に簡単で、機能しない日まで機能すると思い込んでしまいます。

Unicode コンソーシアムでさえ、実際の標準と 100% 互換性がなくなったことを発見した後、サンプル コードを削除しました。

Unicode 標準は常に進化しており、非常に微妙になっています。まとめて実行できる実装は、適切に準拠しているとは言えず、極端な努力によってそれを管理したとしても、準拠したままになります。

于 2012-05-18T22:58:11.663 に答える
183

別のオプションは、次を使用することjqです。

$ printf %s 'encode this'|jq -sRr @uri
encode%20this
$ jq -rn --arg x 'encode this' '$x|@uri'
encode%20this

-r( --raw-output) は、JSON 文字列リテラルではなく、文字列の生の内容を出力します。-n( --null-input) は、STDIN からの入力を読み取りません。

-R( --raw-input) 入力行を JSON として解析する代わりに文字列として扱い、-sR( --slurp --raw-input) 入力を 1 つの文字列に読み取ります。入力に1行しか含まれていない場合、または改行を次のように置き換えたくない場合は、次のように置き換えることができます-sRr-Rr%0A

$ printf %s\\n 'multiple lines' 'of text'|jq -Rr @uri
multiple%20lines
of%20text
$ printf %s\\n 'multiple lines' 'of text'|jq -sRr @uri
multiple%20lines%0Aof%20text%0A

または、これはすべてのバイトをパーセント エンコードします。

xxd -p|tr -d \\n|sed 's/../%&/g'
于 2015-12-22T02:33:08.383 に答える
100

bash スクリプトの 2 行目にPerl のURI::Escapeモジュールとuri_escape関数を使用します。

...

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

編集:コメントでChris Johnsenが提案したように、引用の問題を修正してください。ありがとう!

于 2008-11-18T09:34:45.997 に答える
77

バリアントの 1 つは、醜いかもしれませんが、単純です。

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
}

たとえば、ワンライナーバージョンは次のとおりです(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/'
于 2012-05-29T11:11:23.320 に答える
73

完全を期すために、多くのソリューションは特殊な文字セットを使用するsedか、またはawk変換するだけであるため、コードサイズが非常に大きく、エンコードする必要がある他の特殊文字も変換しません。

urlencode の安全な方法は、すべてのバイトをエンコードすることです。

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

xxd はここで、入力が文字ではなくバイトとして処理されるように注意しています。

編集:

xxd には Debian の vim-common パッケージが付属していますが、それがインストールされていないシステムにいて、インストールしたくありませんでした。hexdump別の方法は、Debian の bsdmainutils パッケージから使用することです。次のグラフによると、bsdmainutils と vim-common がインストールされる可能性はほぼ同じです。

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

ただし、ここではhexdump代わりに使用し、呼び出しxxdを回避できるバージョンがあります。tr

echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'
于 2011-09-21T21:10:50.273 に答える
58

私はそれがPythonでより読みやすいと思います:

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

トリプル'は、値の一重引用符が問題にならないことを保証します。urllibは標準ライブラリにあります。たとえば、このクレイジーな(現実の)URLで機能します。

"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7
于 2010-02-10T10:26:10.760 に答える
36

次のスニペットは、URI::Escape がインストールされていない可能性のある一連のプログラム呼び出しに貼り付けるのに役立つことがわかりました。

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

ソース

于 2009-11-10T19:48:37.217 に答える
28

requestを実行して純粋な curl を使用する場合は、@Jacob のソリューションGETに追加するだけです。--get

次に例を示します。

curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed
于 2011-02-25T12:37:16.197 に答える
16

これは最高のものかもしれません:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
于 2013-08-01T09:14:56.417 に答える
15

awkバージョンへの直接リンク: http://www.shelldorado.com/scripts/cmds/urlencode

:
##########################################################################
# 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 ""
    }
' "$@"
于 2008-11-30T21:42:37.960 に答える
12

外部プログラムを呼び出さない Bash ソリューションを次に示します。

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"
}
于 2017-01-01T02:44:09.043 に答える
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')

これにより、$ 1内の文字列がエンコードされ、$urlに出力されます。必要に応じてvarに入れる必要はありませんが。ところで、それがスペースに変わると思ったタブのsedは含まれていませんでした

于 2011-01-11T12:51:29.847 に答える
10

シェルスクリプトからのphpの使用:

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"
  1. http://www.php.net/manual/en/function.rawurlencode.php
  2. http://www.php.net/manual/en/function.rawurldecode.php
于 2012-01-31T23:10:59.600 に答える
8

Perl に依存したくない場合は、sed を使用することもできます。各文字を個別にエスケープする必要があるため、少し面倒です。以下の内容のファイルを作成して呼び出します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

それを使用するには、次のようにします。

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"

これにより、文字列がエンコードが必要な部分と問題のない部分に分割され、必要な部分がエンコードされてから、つなぎ合わされます。

便宜上、それをshスクリプトに入れることができます。エンコードするパラメーターを取り、パスに配置してから、次のように呼び出すことができます。

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

ソース

于 2008-11-17T19:42:51.823 に答える
7

perl を必要としないソリューションを探している方のために、hexdump と 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
   '`
}

ネット上のいくつかの場所といくつかのローカル試行錯誤からつなぎ合わせました。それはうまくいきます!

于 2010-06-20T00:22:08.960 に答える
7

uni2asciiはとても便利です:

$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
于 2012-11-26T08:48:57.270 に答える
7

encodeURIComponentperl でJavaScript をエミュレートできます。コマンドは次のとおりです。

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

これを で bash エイリアスとして設定できます.bash_profile

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

これで、次のようにパイプできますencodeURIComponent:

$ echo -n 'hèllo wôrld!' | encodeURIComponent
h%C3%A8llo%20w%C3%B4rld!
于 2015-01-20T21:08:51.677 に答える
4

これを行うPOSIX関数は次のとおりです。

url_encode() {
   awk 'BEGIN {
      for (n = 0; n < 125; n++) {
         m[sprintf("%c", n)] = n
      }
      n = 1
      while (1) {
         s = substr(ARGV[1], n, 1)
         if (s == "") {
            break
         }
         t = s ~ /[[:alnum:]_.!~*\47()-]/ ? t s : t sprintf("%%%02X", m[s])
         n++
      }
      print t
   }' "$1"
}

例:

value=$(url_encode "$2")
于 2016-12-31T05:14:25.560 に答える
3

Ruby、完全を期すために

value="$(ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "$2")"
于 2012-06-19T23:45:26.590 に答える
0

この場合、ホスト名を URL エンコードする必要がありました。理由を聞かないでください。ミニマリストであり、Perl ファンでもある私が思いついたのは次のとおりです。

url_encode()
  {
  echo -n "$1" | perl -pe 's/[^a-zA-Z0-9\/_.~-]/sprintf "%%%02x", ord($&)/ge'
  }

私にとって完璧に機能します。

于 2021-10-15T14:46:46.433 に答える