2012-04-07 13 views
25

Estoy usando git, y luego publico el mensaje de confirmación y otros bits como una carga JSON en un servidor.Escapar caracteres en bash (para JSON)

Actualmente tengo:

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

que establece MSG a algo como:

Calendar can't go back past today 

continuación

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

Mi verdadero JSON tiene un par de campos.

Esto funciona bien, pero por supuesto cuando tengo un mensaje de confirmación como el de arriba con un apóstrofo, el JSON no es válido.

¿Cómo puedo escapar de los caracteres requeridos en bash? No estoy familiarizado con el idioma, por lo que no estoy seguro de por dónde empezar. Reemplazar ' con \' haría el trabajo como mínimo, sospecho.

+4

Como una nota extra, JSON se supone que utilizar el doble (no única) cita en torno a valores, por lo que muchos (pero no todos) los analizadores rechazaría lo anterior, incluso si estaba en buenas condiciones estructurales y es cubierto correctamente. – polm23

Respuesta

9

OK, se enteraron de qué hacer. Bash admite esto de forma nativa como se esperaba, aunque, como siempre, ¡la sintaxis no es muy fácil de adivinar!

Esencialmente ${string//substring/replacement} declaraciones de lo que cabría imagen, por lo que puede utilizar

MSG=${MSG//\'/\\\'} 

Para ello. El siguiente problema es que la primera expresión regular ya no funciona, pero que puede ser sustituido por

git log -n 1 --pretty=format:'%s' 

Al final, ni siquiera necesito para escapar de ellos. En cambio, cambié todo el 'en el JSON a \'. Bueno, aprendes algo todos los días.

+0

Esto de ninguna manera es totalmente compatible con el escape JSON. Lo real requiere que las pestañas se reemplacen por '\ t', que se reemplacen las líneas nuevas por' \ n', que se dupliquen las barras invertidas literales, etc. –

2

me encontré con algo así:

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

MSG termina estando bien - Supongo que solo quiero hacer algo como MSG = MSG.replace ("'", "\'"), pero no estoy seguro de cómo hacerlo en bash. –

+0

Aquí hay algunos puntos http://www.cyberciti.biz/faq/unix-linux-replace-string-words-in-many-files/. Puede ser que pueda captarlo más rápido – user907860

12

También estaba tratando de escapar de los personajes en Bash, para transferirlos usando JSON, cuando me encontré con esto. Encontré que en realidad hay una mayor list of characters that must be escaped – especialmente si usted está tratando de manejar texto de forma libre

hay dos consejos que he encontrado útil:.

  • Utilice la sintaxis Bash ${string//substring/replacement} descrito en este hilo
  • . Use el control real ch aracters para la lengüeta, de nueva línea, retorno de carro, etc. En vim puede introducir estos escribiendo Ctrl + V seguido por el código de control real (Ctrl + I para la lengüeta por ejemplo).

Los reemplazos del golpe resultantes se me ocurrió son los siguientes:

JSON_TOPIC_RAW=${JSON_TOPIC_RAW//\\/\\\\} # \ 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//\//\\\/} #/
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//\'/\\\'} # ' (not strictly needed ?) 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//\"/\\\"} # " 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW// /\\t} # \t (tab) 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW// 
/\\\n} # \n (newline) 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//^M/\\\r} # \r (carriage return) 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//^L/\\\f} # \f (form feed) 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//^H/\\\b} # \b (backspace) 

no he trabajado en esta etapa la manera de escapar correctamente los caracteres Unicode que también es (aparentemente) requerido.Actualizaré mi respuesta si resuelvo esto.

+0

Con respecto a la sugerencia de que use el parámetro -d y el modificador @ con curl, eso no resuelve el problema. De hecho, ya estaba usando esto y descubrí que el contenido del archivo aún necesita ser codificado correctamente de la manera que JSON espera. – xsgordon

20

En lugar de preocuparse por cómo citar correctamente los datos, simplemente guárdelos en un archivo y utilice la construcción @ que curl permite con la opción --data. Para garantizar que la salida de git se escape correctamente para usar como un valor JSON, use una herramienta como jq para generar el JSON, en lugar de crearlo manualmente.

jq --arg msg "$(git log -n 1 --format=oneline | grep -o ' .\+')" \ 
    '{payload: { message: $msg }}' > git-tmp.txt 

curl -i -X POST \ 
    -H 'Accept: application/text' \ 
    -H 'Content-type: application/json' \ 
    -d @git-tmp.txt \ 
    'https://example.com' 

También puede leer directamente desde la entrada estándar usando -d @-; Lo dejo como un ejercicio para que el lector construya la tubería que lee desde git y produce el mensaje de carga útil correcto para cargar con curl.

(pista: es jq ... | curl ... [email protected] 'https://example.com')

+0

Correcto; No pensé en eso cuando escribí esta respuesta. Actualizaré ahora. – chepner

41

utilizar Python:

Esta solución no es pura fiesta, pero es no invasivo y maneja Unicode.

json_escape() { 
    printf '%s' $1 | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))' 
} 

Tenga en cuenta que JSON es parte de las bibliotecas estándar de Python y ha sido durante mucho tiempo, por lo que esta es una dependencia bastante escaso pitón.

O usando PHP:

json_escape() { 
    printf '%s' $1 | php -r 'echo json_encode(file_get_contents("php://stdin"));' 
} 

uso de este modo:

$ json_escape "ヤホー" 
"\u30e4\u30db\u30fc" 
+0

¿Es realmente un formato JSON el que se pasa como primer parámetro, o es un formato de objeto python? ¿Hay una diferencia apreciable entre los dos? –

+1

El primer parámetro debe ser solo una cadena que será un valor simple en el resultado JSON, no un objeto complejo en sí mismo, al igual que en la pregunta original. Si desea insertar un valor complejo, bash es casi seguro un problema mayor de lo que vale. – polm23

+1

Me gusta esto. no es difícil cambiarlo a un simple oneliner: 'alias json_escape =" python -c 'import json, sys; imprimir json.dumps (sys.stdin.read())' "' –

3

La forma más simple está utilizando jshon una herramienta de línea comando para analizar, leer y crear JSON.

jshon -s 'Your data goes here.' 2>/dev/null

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

La línea anterior funciona para mí. consulte https://github.com/stedolan/jq para obtener más jq herramientas

0

Tuve la misma idea de enviar un mensaje con el mensaje de confirmación después de la confirmación. Primero intenté que era similar al autor aquí. Pero luego se encontró una solución mejor y más simple.

Acaba de crear el archivo php que está enviando un mensaje y lo llama con wget. en ganchos/post-reciben:

wget -qO - "http://localhost/git.php" 

en git.php:

chdir("/opt/git/project.git"); 
$git_log = exec("git log -n 1 --format=oneline | grep -o ' .\+'"); 

y luego crear JSON y llamar CURL en estilo PHP

0

Esta es una solución de escapar usando Perl que se escapa de la barra invertida (\), de comillas dobles (") y caracteres de control U+0000-U+001F:

$ echo -ne "Hello, \n\tBye" | \ 
    perl -pe 's/(\\(\\\\)*)/$1$1/g; s/(?!\\)(["\x00-\x1f])/sprintf("\\u%04x",ord($1))/eg;' 
Hello, \u000a\u0009Bye 
Cuestiones relacionadas