Подтвердить что ты не робот

Экранирование символов в bash (для JSON)

Я использую git, а затем отправляю сообщение фиксации и другие биты в качестве полезной нагрузки JSON на сервер.

В настоящее время у меня есть:

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

который устанавливает MSG в нечто вроде:

Calendar can't go back past today

затем

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

В моем реальном JSON есть еще несколько полей.

Это отлично работает, но, конечно, когда у меня есть сообщение фиксации, такое как выше, с апострофом в нем, JSON недействителен.

Как я могу избежать символов, требуемых в bash? Я не знаком с языком, поэтому не знаю, с чего начать. Замена ' на \' выполнила бы работу, как я подозреваю.

4b9b3361

Ответ 1

ОК, выяснилось, что делать. Bash поддерживает это как ожидалось, хотя, как всегда, синтаксис на самом деле не очень угадан!

По существу ${string//substring/replacement} возвращает то, что вы хотите, так что вы можете использовать

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

Для этого. Следующая проблема заключается в том, что первое регулярное выражение больше не работает, но его можно заменить на

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

В конце концов, мне даже не нужно было бежать от них. Вместо этого я просто поменял все "в JSON". Ну, вы узнаете что-то каждый день.

Ответ 2

Использование Python:

Это решение не является чисто bash, но оно неинвазивно и обрабатывает юникод.

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

Обратите внимание, что JSON является частью стандартных библиотек Python и существует уже долгое время, так что это довольно минимальная зависимость от Python.

Или используя PHP:

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

Используйте вот так:

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

Ответ 3

Вместо того, чтобы беспокоиться о том, как правильно процитировать данные, просто сохраните их в файл и используйте конструкцию @, которую допускает curl с опцией --data. Чтобы убедиться, что вывод git правильно экранирован для использования в качестве значения JSON, используйте инструмент наподобие jq для генерации JSON вместо его создания вручную.

jq -n --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'

Вы также можете читать напрямую из стандартного ввода, используя -d @-; Я оставляю это в качестве упражнения для читателя, чтобы построить конвейер, который читает из git и производит правильное сообщение полезной нагрузки для загрузки с curl.

(Подсказка: это jq ... | curl ... [email protected] 'https://example.com')

Ответ 4

jq может это сделать.

Облегченный, бесплатный и написанный на C, jq пользуется широкой поддержкой сообщества с более чем 15 тысячами звезд на GitHub. Лично я нахожу это очень быстрым и полезным в моей повседневной работе.

Преобразовать строку в JSON

jq -aRs . <<< '猫に小判'

Чтобы объяснить,

  • -a означает "вывод ascii"
  • -R означает "необработанный ввод"
  • -s означает "включать разрывы строк"
  • . означает "вывести корень документа JSON"
  • <<< передает строку в стандартный ввод (только bash?)

Вариант использования Git + Grep

Чтобы исправить пример кода, заданный OP, просто пройдите через jq.

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

Ответ 5

Я также пытался экранировать символы в Bash для передачи с помощью JSON, когда натолкнулся на это. Я обнаружил, что на самом деле существует больший список символов, которые необходимо экранировать & ndash; особенно если вы пытаетесь обрабатывать текст произвольной формы.

Вот два совета, которые я нашел полезными:

  • Используйте синтаксис Bash ${string//substring/replacement}, описанный в этой теме.
  • Используйте фактические управляющие символы для табуляции, новой строки, возврата каретки и т.д. В vim вы можете ввести их, набрав Ctrl + V, а затем действительный управляющий код (например, Ctrl + I для табуляции).

В результате были получены следующие замены Bash:

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)

На данном этапе я не разобрался, как правильно экранировать символы Юникода, что также (очевидно) необходимо. Я обновлю свой ответ, если я решу это.

Ответ 6

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

Приведенная выше строка работает для меня. Ссылаться на https://github.com/stedolan/jq для других инструментов jq

Ответ 7

Я нашел что-то вроде этого:

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

Ответ 8

Самый простой способ - использовать jshon, инструмент командной строки для анализа, чтения и создания JSON.

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

Ответ 9

У меня была такая же идея отправить сообщение с сообщением commit после фиксации. Сначала я попробовал подобное, как автор. Но позже было найдено лучшее и более простое решение.

Только что созданный php файл, который отправляет сообщение и вызывает его с помощью wget. в hooks/post-receive:

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

в git.php:

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

И затем создайте JSON и вызовите CURL в стиле PHP

Ответ 10

Это решение, использующее Perl, которое ускоряет обратную косую черту (\), двойную кавычку (") и управляющие символы 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