Как использовать shell-скрипт в качестве хост-приложения Chrome Native Messaging
Как вы обрабатываете вызов API Chrome Native Messaging с помощью bash-скрипта?
Мне удалось сделать это с Python на этом примере
Конечно, я могу позвонить bash
из кода Python с subprocess
, но возможно ли пропустить python и обработать сообщение в bash
напрямую?
Проблемной частью является чтение сериализованного сообщения JSON в переменную. Сообщение сериализуется с использованием JSON в кодировке UTF-8 и ему предшествует 32-битная длина сообщения в собственном байтовом порядке через stdin.
echo $*
только выходы:chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/
Также что-то вроде
read
echo $REPLY
ничего не выводит. Никаких признаков сообщения JSON. Python использует struct.unpack
за это. Можно ли это сделать в bash
?
2 ответа
Я предлагаю не использовать сценарии оболочки (bash) в качестве собственного хоста обмена сообщениями, поскольку bash слишком ограничен, чтобы быть полезным.
read
без каких-либо параметров считывает целую строку перед завершением, в то время как собственный протокол обмена сообщениями указывает, что первые четыре байта указывают длину следующего сообщения (в собственном порядке байтов).
Bash - ужасный инструмент для обработки двоичных данных. Улучшенная версия вашего read
Команда будет указывать -n N
параметр, чтобы остановить чтение после N
символы (примечание: не байты) и -r
удалить некоторую обработку. Например, следующее будет хранить первые четыре символа в переменной с именем var_prefix
:
IFS= read -rn 4 var_prefix
Даже если вы предполагаете, что это сохраняет первые четыре байта в переменной (это не так!), Вам придется конвертировать байты в целое число. Я уже упоминал, что bash автоматически сбрасывает все байты NUL? Эти характеристики делают Bash совершенно бесполезным для того, чтобы быть полностью способным нативным хостом обмена сообщениями.
Вы можете справиться с этим недостатком, проигнорировав первые несколько байтов, и начните анализировать результат, когда заметите {
символ, начало запроса в формате JSON. После этого вы должны прочитать весь ввод, пока не будет найден конец ввода. Вам нужен анализатор JSON, который прекращает чтение ввода, когда встречается с концом строки JSON. Удачи в написании этого.
Генерация вывода проще, просто используйте echo -n
или же printf
,
Вот минимальный пример, который предполагает, что ввод заканчивается }
, читает его (без обработки) и отвечает с результатом. Хотя эта демонстрация работает, я настоятельно рекомендую не использовать bash, а использовать более богатый язык сценариев, такой как Python или C++.
#!/bin/bash
# Loop forever, to deal with chrome.runtime.connectNative
while IFS= read -r -n1 c; do
# Read the first message
# Assuming that the message ALWAYS ends with a },
# with no }s in the string. Adopt this piece of code if needed.
if [ "$c" != '}' ] ; then
continue
fi
message='{"message": "Hello world!"}'
# Calculate the byte size of the string.
# NOTE: This assumes that byte length is identical to the string length!
# Do not use multibyte (unicode) characters, escape them instead, e.g.
# message='"Some unicode character:\u1234"'
messagelen=${#message}
# Convert to an integer in native byte order.
# If you see an error message in Chrome's stdout with
# "Native Messaging host tried sending a message that is ... bytes long.",
# then just swap the order, i.e. messagelen1 <-> messagelen4 and
# messagelen2 <-> messagelen3
messagelen1=$(( ($messagelen ) & 0xFF ))
messagelen2=$(( ($messagelen >> 8) & 0xFF ))
messagelen3=$(( ($messagelen >> 16) & 0xFF ))
messagelen4=$(( ($messagelen >> 24) & 0xFF ))
# Print the message byte length followed by the actual message.
printf "$(printf '\\x%x\\x%x\\x%x\\x%x' \
$messagelen1 $messagelen2 $messagelen3 $messagelen4)%s" "$message"
done
Чтение и запись сообщений можно выполнять на чистом bash без использования каких-либо подоболочек.
Сложнее всего прочитать первые 4 байта, но это можно сделать, установивLC_ALL=C
, что заставит bash обрабатывать каждый байт как символ и использовать следующие параметры для :
-
-n 1
читать по одному символу (=байту) за раз -
-r
для предотвращения обработки обратной косой черты -
-d ''
иметьread
вернуть 0 после чтения нулевого байта
Мы можем преобразовать символ в его числовое представление с помощьюprintf %d "'C"
.
После того, как мы получим длину, мы можем использоватьread -r -N "$len"
чтобы прочитать именно столько байтов из ввода.
LC_ALL=C IFS=
readmsg() {
# return non-zero if fewer than 4 bytes of input available
# or if message is shorter than length specified by first
# 4 bytes of input
# otherwise, set the variable $msg and return 0
local -i i n len=0
for ((i=0; i<4; i++)); do
read -r -d '' -n 1 || return
printf -v n %d "'$REPLY"
len+='n<<i*8'
done
read -r -N "$len" && ((${#REPLY}==len)) && msg=$REPLY
}
sendmsg() {
local x
printf -v x %08X "${#1}"
printf %b%s "\x${x:6:2}\x${x:4:2}\x${x:2:2}\x${x:0:2}" "$1"
}
while readmsg; do
# do_something "$msg"
response='{"echo": "foo"}'
sendmsg "$response"
done