108 votes

Échapper les caractères en bash (pour JSON)

Je suis en train d'utiliser git, puis d'envoyer le message de validation et d'autres éléments sous forme de charge utile JSON à un serveur.

Actuellement, j'ai:

MSG=`git log -n 1 --format=oneline | grep -o ' .\+'`

qui définit MSG à quelque chose comme:

Le calendrier ne peut pas revenir en arrière jusqu'à aujourd'hui

puis

curl -i -X POST \
  -H 'Accept: application/text' \
  -H 'Content-type: application/json' \
  -d "{'payload': {'message': '$MSG'}}" \
  'https://example.com'

Mon vrai JSON a quelques autres champs.

Cela fonctionne bien, mais bien sûr lorsque j'ai un message de validation comme celui ci-dessus avec une apostrophe, le JSON n'est pas valide.

Comment puis-je échapper les caractères requis en bash ? Je ne suis pas familier avec le langage, donc je ne sais pas par où commencer. Remplacer ' par \' ferait probablement l'affaire au minimum, je suppose.

9voto

Levi Points 1
git log -n 1 --format=oneline | grep -o ' .\+' | jq --slurp --raw-input

La ligne ci-dessus fonctionne pour moi. Consultez https://github.com/stedolan/jq pour plus d'outils jq

5voto

user907860 Points 1758

J'ai trouvé quelque chose comme ça :

MSG=`echo $MSG | sed "s/'/\\\\\'/g"`

4voto

jw23 Points 1359

La manière la plus simple est d'utiliser jshon, un outil en ligne de commande pour analyser, lire et créer du JSON.

jshon -s 'Vos données vont ici.' 2>/dev/null

3voto

Fravadona Points 970

Ajouter un outil compatible JSON à votre environnement est parfois impossible, alors voici une solution POSIX, sous forme de fonction shell, qui devrait fonctionner sur tous les UNIX/Linux :

json_stringify() {
    LANG=C command -p awk '
        BEGIN {
            ORS = ""

            for ( i = 1; i <= 127; i++ )
                tr[ sprintf( "%c", i) ] = sprintf( "\\u%04x", i )

            for ( i = 1; i < ARGC; i++ ) {
                s = ARGV[i]
                print "\""
                while ( match( s, /[\001-\037\177"\\]/ ) ) {
                    print substr(s,1,RSTART-1) tr[ substr(s,RSTART,RLENGTH) ]
                    s = substr(s,RSTART+RLENGTH)
                }
                print s "\"\n"
            }
        }
    ' "$@"
}

Note : Vous pouvez préférer utiliser le largement disponible (mais non-POSIX) perl à la place :

json_stringify() {
    LANG=C perl -le '
        for (@ARGV) {
            s/[\x00-\x1f\x7f"\\]/sprintf("\\u%04x",ord($&))/ge;
            print "\"$_\""
        }
    ' -- "$@"
}
Exemple :
json_stringify '"foo\bar"' 'hello
world'

Chaque argument est converti en une chaîne JSON et affiché une par ligne :

"\u0022foo\u005cbar\u0022"
"hello\u000aworld"
Limitations :
  • Ne peut pas traiter les octets NUL.

  • Ne valide pas l'entrée pour UNICODE; elle n'effectue qu'un échappement des caractères ASCII obligatoires spécifiés par le RFC 8259.

  • L'entrée est limitée en taille (vous obtiendrez une erreur Liste d'arguments trop longue lorsque l'entrée est trop grande).


Réponse à la question de l'OP :

Voici comment vous pouvez construire un objet JSON valide en utilisant la fonction json_stingify :

MSG=$(git log -n 1 --format=oneline | grep -o ' .\+')

curl -i -X POST \
  -H 'Accept: application/text' \
  -H 'Content-type: application/json' \
  -d '{"payload": {"message": '"$(json_stringify "$MSG")"'}}' \
  'https://example.com'

2voto

Reino Points 239

[...] avec une apostrophe dedans, le JSON est invalide.

Pas selon https://www.json.org. Une apostrophe est autorisée dans une chaîne JSON.

Comment puis-je échapper les caractères nécessaires dans bash ?

Vous pouvez utiliser xidel pour préparer correctement le JSON que vous souhaitez envoyer via POST.
Comme https://example.com ne peut pas être testé, je vais utiliser https://api.github.com/markdown (voir cette réponse) comme exemple.

Supposons 'çömmít' "mêssågè" comme la sortie exotique de git log -n 1 --pretty=format:'%s'.

Créez l'objet JSON (sérialisé) avec la valeur de l'attribut "text" correctement échappé :

$ git log -n 1 --pretty=format:'%s' | \
  xidel -se 'serialize({"text":$raw},{"method":"json","encoding":"us-ascii"})'
{"text":"'\u00E7\u00F6mm\u00EDt' \"m\u00EAss\u00E5g\u00E8\""}

Curl (variable)

$ eval "$(
  git log -n 1 --pretty=format:'%s' | \
  xidel -se 'msg:=serialize({"text":$raw},{"method":"json","encoding":"us-ascii"})' --output-format=bash
)"

$ echo $msg
{"text":"'\u00E7\u00F6mm\u00EDt' \"m\u00EAss\u00E5g\u00E8\""}

$ curl -d "$msg" https://api.github.com/markdown
'çömmít' "mêssågè"

Curl (pipe)

$ git log -n 1 --pretty=format:'%s' | \
  xidel -se 'serialize({"text":$raw},{"method":"json","encoding":"us-ascii"})' | \
  curl -d@- https://api.github.com/markdown
'çömmít' "mêssågè"

En réalité, il n'est pas nécessaire d'utiliser curl si vous utilisez déjà xidel.

Xidel (pipe)

$ git log -n 1 --pretty=format:'%s' | \
  xidel -s \
  -d '{serialize({"text":read()},{"method":"json","encoding":"us-ascii"})}' \
  "https://api.github.com/markdown" \
  -e '$raw'
'çömmít' "mêssågè"

Xidel (pipe, in-query)

$ git log -n 1 --pretty=format:'%s' | \
  xidel -se '
    x:request({
      "post":serialize(
        {"text":$raw},
        {"method":"json","encoding":"us-ascii"}
      ),
      "url":"https://api.github.com/markdown"
    })/raw
  '
'çömmít' "mêssågè"

Xidel (all in-query)

$ xidel -se '
  x:request({
    "post":serialize(
      {"text":system("git log -n 1 --pretty=format:'\''%s'\''")},
      {"method":"json","encoding":"us-ascii"}
    ),
    "url":"https://api.github.com/markdown"
  })/raw
'
'çömmít' "mêssågè"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X