【问题标题】:jq or xsltproc alternative for s-expressions?jq 或 xsltproc 替代 s 表达式?
【发布时间】:2015-07-05 17:03:44
【问题描述】:

根据 Unix 哲学,我有一个项目,其中包含一堆使用 bash 脚本捆绑在一起的小程序。他们的交换格式最初是这样的:

meta1a:meta1b:meta1c AST1
meta2a:meta2b:meta2c AST2

:-separated 字段是元数据,ASTs 是脚本按原样传递的 s 表达式。这很好用,因为我可以使用cut -d ' ' 将元数据从 AST 中分离出来,并使用cut -d ':' 来挖掘元数据。但是,我随后需要添加一个包含空格的元数据字段,这会破坏这种格式。由于没有字段使用选项卡,我切换到以下内容:

meta1a:meta1b:meta1c:meta 1 d\tAST1
meta2a:meta2b:meta2c:meta 2 d\tAST2

由于我设想未来会添加更多元数据字段,我认为是时候切换到更结构化的格式,而不是玩“猜标点符号”的游戏。

我可以使用 JSON 和 jq,而不是分隔符和 cut,或者我可以使用 XML 和 xsltproc,但由于我已经在 AST 中使用 s 表达式,我想知道是否有在这里使用它们的好方法?

例如,看起来像这样的东西:

(echo '(("foo1" "bar1" "baz1" "quux 1") ast1)'
 echo '(("foo2" "bar2" "baz2" "quux 2") ast2)') | sexpr 'caar'

"foo1"
"foo2"

我的要求是:

  • 以最少的样板直接使用 stdio,因为这是我的程序读取/写入其数据的地方
  • 可从 shell 脚本轻松调用为 bash 的进程调用和流水线提供了一个非常引人注目的替代方案
  • 如果可能,流式 I/O; IE。我宁愿一次使用一个 AST,而不是消耗整个输入来寻找结束 )
  • 快速且轻量级,尤其是在它被多次调用的情况下;每个 AST 只有几 KB,但它们加起来可以达到数百 MB
  • 至少应该在 Linux 上工作;跨平台就好了

显而易见的选择是使用 Lisp/Scheme 解释器,但我使用过的唯一一个是 Emacs,它太重量级了。也许另一种实现更轻量级并且更适合这个?

在 Haskell 中,我使用过 shelly、turtle 和 atto-lisp,但我的大部分代码都用于在 String/Text/ByteString 之间转换、包装/解包 Lisps、实现我自己的 car、@987654334 @、cons

我读过一些关于 scsh 的文章,但也不知道这是否合适。

【问题讨论】:

    标签: bash shell lisp s-expression


    【解决方案1】:

    你可以试试 Common Lisp。

    以最少的样板直接使用 stdio,因为那是 我的程序在哪里读/写他们的数据

    (loop for (attributes ast) = (safe-read) do (print ...)
    
    • 从标准输入和输出读取/写入。
    • safe-read 应该在读取时禁用代码的执行。至少有one implementation。不要直接eval你的 AST,除非你完全知道里面有什么。

    可从 shell 脚本轻松调用或提供非常引人注目的 替代 bash 的进程调用和流水线

    本着与java -jar ... 相同的精神,您可以启动您的 Common Lisp 可执行文件,例如sbcl,在参数中有一个脚本:sbcl --load file.lisp。您甚至可以转储应用程序的核心或可执行核心并预加载所有内容 (save-lisp-and-die)。 或者,使用cl-launch,它可以自动、可移植地执行上述操作,并生成 shell 脚本和/或从您的代码生成可执行程序。

    如果可能的话,流式 I/O; IE。我宁愿一次使用一个 AST 而不是消耗整个输入来寻找结束)

    如果整个输入流以( 开头,那么read 将读取到结束的) 字符,但实际上很少这样做:Common Lisp 中的源代码不是包含在一对中每个文件的括号,但作为一个形式的序列。如果您的流产生的不是一个而是多个 s-exp,则阅读器将一次读取一个。

    快速且轻量级,尤其是在它被多次调用的情况下; 每个 AST 只有几个 KB,但它们加起来可以达到数百 MB

    它会很快,特别是如果你保存一个核心。轻量级,众所周知,lisp 图像会占用一些磁盘空间(例如 46MB),但这很少成为问题。为什么重要?也许您对轻量级的含义有另一种定义,因为这与您将要解析的 AST 的大小无关。不过,阅读那些 AST 应该没有问题。

    至少应该在 Linux 上工作;跨平台就好了

    Wikipedia。例如,Clozure CL (CCL) 在 Mac OS X、FreeBSD、Linux、Solaris 和 Windows 上运行,32/64 位。

    【讨论】:

      【解决方案2】:

      在处理一个稍微不同的任务时,我再次发现需要处理一堆 s 表达式。这次我需要对给定的 s 表达式执行一些重要的处理(提取使用的符号列表等),而不是选择将它们作为不透明的字符串传递。

      我尝试了 Racket 并感到惊喜;它比我以前使用过的其他 Lisps(Emacs Lisp 和各种特定于应用程序的 Scheme 脚本)要好得多,因为它有很好的文档和一个包含电池的标准库。

      此类任务的一些相关要点:

      • 用于读取和写入数据的“端口”。这些可以(动态地?)在表达式中限定范围,并且默认为 stdio(即 (current-input-port) 默认为标准输入,(current-output-port) 默认为标准输出)。端口使 stdio 和文件访问与 shell 一样好用:更冗长,但更少粗糙的边缘情况。
      • port->stringfile->linesread 等各种转换函数可以轻松获取适当粒度形式(字符、行、字符串、表达式等)的数据。
      • 我找不到读取多个 s 表达式的“标准”方法,因为read 只返回一个,因此需要迭代/递归以流式方式执行此操作。
      • 如果不需要流式传输,我发现最简单的方法是将整个输入读取为字符串,附加"(\n""\n)",然后使用(with-input-from-string my-modified-input read) 获得一个大列表。

      我发现 Racket 的启动时间很慢,所以如果速度是一个问题,我不建议反复调用脚本作为循环的一部分。将我的循环移动到 Racket 并调用一次脚本很容易。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-26
        • 2018-03-02
        • 2016-12-22
        • 1970-01-01
        相关资源
        最近更新 更多