Сценарий оболочки, экранировать новые строки, но испускать другие?

Учитывая имя файла, я хочу написать сценарий оболочки, который выдает следующее и направляет его в процесс:

Content-Length:<LEN><CR><LF>
<CR><LF>
{ "jsonrpc":"2.0", "params":{ "text":"<ESCAPED-TEXT>" } }

где <ESCAPED-TEXT> — это содержимое файла, но его CR, LF и кавычки были экранированы как \r, \n и \" (и я предполагаю, что все остальные экраны JSON в конечном итоге также понадобятся), и где <LEN> — это длина окончательного JSON. строка, содержащая экранированный текст.

Вот мое текущее решение для bash-скрипта. Это работает, но уродливо, как черт.

(
  TXT=`cat ~/a.py | sed -E -e :a -e '$!N; s/\n/\\\n/g; ta' | sed 's/"/\\\"/g'`
  CMD='{"jsonrpc":"2.0", "params":{ "text":{"'${TXT}'"}} }'
  printf "Content-Length: ${#CMD}\r\n\r\n"
  echo -n "${CMD}"
) | pyls

Кто-нибудь может подсказать, как сделать этот очиститель, пожалуйста?

  • Этот сценарий sed заменяет только LF, а не CR. Он накапливает каждую строку в буфере, а затем выполняет s//g для замены всех LF в нем. Я не мог придумать ничего более чистого, что работало бы как на Linux, так и на OSX/BSD.

  • Я использовал и printf, и эхо. Сначала printf, потому что я действительно хочу выдать CRLFCRLF после заголовка Content-Length, и вам, по-видимому, нужно printf для этого, потому что поведение echo с escape-последовательностями неодинаково во всех платформы. Следующее echo, потому что я не хочу, чтобы литералы \r и \n внутри TXT не экранировались, что сделало бы printf.

Контекст: существует стандарт под названием «Language Server Protocol». В основном вы запускаете что-то вроде pyls, которое я запускаю здесь, и вы передаете JsonRPC к нему через стандартный ввод, и он передает обратно вещи. У разных людей есть написанные языковые серверы для Python (пилы, которые я использую здесь), и C#, и C++, и Typescript, и PHP, и OCaml, и Go, и Java, и каждый человек склонен писать свой языковой сервер по-своему. родной язык.

Я хочу написать тестовую обвязку, которая может отправлять несколько примеров пакетов JsonRPC на любой такой сервер.

Я решил, что будет лучше написать мою тестовую оснастку только с помощью обычных базовых сценариев оболочки, которые доступны на всех платформах из коробки. Таким образом, каждый может использовать мою тестовую систему на своем языковом сервере. (Если бы я вместо этого написал его на Python, скажем, мне было бы проще писать, но это заставило бы людей C# изучать + устанавливать python только для того, чтобы запустить его, а также Typescript, PHP, OCaml, Go и другие близкие.)


person Lucian Wischik    schedule 06.02.2017    source источник


Ответы (3)


a.py:

print("alfa")
print("bravo")

Авк-скрипт:

{
  gsub("\r", "\\r")
  gsub("\42", "\\\42")
  z = z $0 "\\n"
}
END {
  printf "Content-Length: %d\r\n", length(z) + 42
  printf "\r\n"
  printf "{\42jsonrpc\42: \0422.0\42, \42params\42: {\42text\42: \42%s\42}}", z
}

Результат:

Content-Length: 81

{"jsonrpc": "2.0", "params": {"text": "print(\"alfa\")\r\nprint(\"bravo\")\r\n"}}
person Steven Penny    schedule 07.02.2017

Кто-нибудь может подсказать, как сделать этот очиститель, пожалуйста?

Я предполагаю, что все другие побеги JSON в конечном итоге также понадобятся.

Если бы в моем распоряжении уже был Python, я бы очень, очень сильно постарался использовать стандартный Кодер Python JSON, по крайней мере, для экранирующей части строки. Зачем ломать что-то такое, когда можно использовать что-то, что работает, с чем вы уже наполовину знакомы?

Если бы у меня не было Python, мне бы понравилось решение Стива Пенни. Эмпирические правила:

  1. для обработки наборов файлов используйте оболочку
  2. для обработки данных в файле используйте awk
  3. если sed не может сделать это тривиально, см. правило №2

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

person James K. Lowden    schedule 07.02.2017

Я думаю, что основная проблема с вашим скриптом заключается в том, что он не использует строки формата с printf. Обычно printf используется с различными специальными символами в строке формата (например, %s, %b и т. д.) и списком дополнительных аргументов, которые подставляются в строку формата.

То есть, когда вы говорите: «[Я использовал] echo, потому что я не хочу, чтобы литералы \r и \n были не экранированы, что будет делать printf», проблема просто в том, что не используется printf "%s" "$string".

В любом случае, вот идея того, как использовать этот материал, чтобы сделать все в bash без внешних инструментов:

escapes=('\n' '\r' '\"')         # the escapes we want to put into the output

txt=$(< ~/a.py);                 # read the file into a variable
for esc in "${escapes[@]}"; do
    # escapes are evaluated in a %b string w/ printf
    # using -v puts the result into a variable
    printf -v lit '%b' "$esc"
    # use built-in ${string//pattern/replacement} expansion
    txt=${txt//$lit/$esc}
done

txt='{"jsonrpc":"2.0", "params":{ "text":{"'$txt'"}} }'

# escapes in the format string are expanded
# but escapes in the argument substituted for %s are not
printf 'Content-Length: %s\r\n\r\n%s' "${#txt}"

"$текст"

person Grisha Levit    schedule 07.02.2017