Экранирование символов в 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? Я не знаком с языком, поэтому не знаю, с чего начать. Замена '
на \'
выполнила бы работу, как я подозреваю.
Ответы
Ответ 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