【问题标题】:Shell script, escape newlines but emit others?Shell脚本,转义换行符但发出其他行?
【发布时间】:2017-06-23 18:31:56
【问题描述】:

给定一个文件名,我想编写一个 shell 脚本,它发出以下内容,并将其通过管道传输到一个进程中:

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

其中&lt;ESCAPED-TEXT&gt; 是文件的内容,但它的 CR、LF 和引号已转义为 \r\n\"(我猜所有其他 JSON 转义最终也将被需要) ),其中&lt;LEN&gt; 是包含转义文本的最终 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 和 echo。首先 printf 因为我 确实 想在 Content-Length 标头之后发出 CRLFCRLF ,而您显然需要 printf ,因为带有转义的 echo 的行为在整个过程中并不统一平台。接下来 echo 因为我希望 TXT 中的 \r\n 文字不转义,而 printf 会这样做。

上下文:有一个标准叫做“语言服务器协议”。基本上你运行类似pyls 我在这里运行的东西,然后你通过标准输入将JsonRPC 传递给它,然后它通过管道返回东西。不同的人为 Python(我在这里使用的 pyls)、C#、C++、Typescript、PHP、OCaml、Go 和 Java 编写了语言服务器,每个人都倾向于在他们的自己的语言。

我想编写一个测试工具,它可以将一些示例 JsonRPC 数据包发送到任何此类服务器。

我认为最好只用开箱即用的所有平台上可用的通用基本 shell 脚本编写我的测试工具。这样每个人都可以在他们的语言服务器上使用我的测试工具。 (如果我改为在 Python 上编写它,比方说,它会更容易编写,但它会迫使 C# 人学习 + 安装 python 只是为了运行它,以及 Typescript、PHP、OCaml、Go 和其他伙计们。)

【问题讨论】:

    标签: bash shell awk sed tr


    【解决方案1】:

    我认为您的脚本的主要问题是没有在 printf 中使用格式字符串。使用 printf 的常用方法是在 格式字符串 中使用各种特殊字符(如 %s%b 等)以及替换为格式字符串的附加参数列表。

    也就是说,当您说“[I used] 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}"
    

    “$txt”

    【讨论】:

      【解决方案2】:

      谁能建议如何做这个清洁剂?

      我想所有其他的 JSON 转义最终也会需要

      如果我已经可以使用 Python,我会非常非常努力地使用标准 Python JSON encoder,至少对于字符串转义部分。当您可以使用您已经熟悉的已知可行的东西时,为什么要拼凑一些可行的东西?

      如果我没有 Python,我喜欢Steve Penny's 解决方案。经验法则:

      1. 要处理文件集,请使用 shell
      2. 要处理文件中的数据,请使用 awk
      3. 如果 sed 不能轻松完成,请参阅规则 #2

      如果你对awk有点了解,他的解决方案几乎一眼就能理解。我会称之为“清洁工”。如果您不了解 awk,这似乎是结识的绝佳机会。

      【讨论】:

        【解决方案3】:

        a.py:

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

        Awk 脚本:

        {
          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"}}
        

        【讨论】:

          猜你喜欢
          • 2016-05-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多