it-swarm.com.ru

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

38
Rich Bradshaw

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

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

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

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

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

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

10
Rich Bradshaw

Использование 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"
49
polm23

Вместо того, чтобы беспокоиться о том, как правильно заключить в кавычки данные, просто сохраните их в файл и используйте конструкцию @, которую 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')

34
chepner

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

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

  • Используйте синтаксис 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)

В этом случае необходимо (по-видимому). Я обновлю свой ответ, если я решу это.

16
xsgordon

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

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

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

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

Объяснить,

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

Git + Curl Use Case

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

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

Я нашел что-то подобное:

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

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

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

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

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

4
wcy

Это экранирующее решение с использованием 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
0
Josh Bode

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

Только что создал 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

0
KirillDE