s表达式的jq或xsltproc替代?

时间:2015-07-05 17:03:45

标签: bash shell lisp s-expression

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

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

: - 分隔的字段是元数据,而AST是脚本按原样传递的s表达式。这很好,因为我可以使用cut -d ' '从AST中分割元数据,并cut -d ':'来挖掘元数据。但是,我需要添加一个包含空格的元数据字段,这会打破这种格式。由于没有字段使用制表符,我切换到以下内容:

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

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

而不是分隔符和cut我可以使用JSON和jq,或者我可以使用XML和xsltproc,但是因为我已经在使用s表达式来表示AST ,我想知道是否有一种很好的方式在这里使用它们?

例如,看起来像这样:

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

"foo1"
"foo2"

我的要求是:

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

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

在Haskell中,我玩过shelly,turtle和atto-lisp,但我的大部分代码都花在String / Text / ByteString之间转换,包装/解包Lisp s,实现我自己的{{} 1}},carcdr

我已经读过一些关于scsh的内容,但不知道这是否合适。

2 个答案:

答案 0 :(得分: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;即。我宁愿一次使用一个AST   而不是消耗整个输入寻找结束)

如果整个输入流以(开头,则read将读取结束)字符,但实际上很少这样做:Common Lisp中的源代码并未包含在一对括号中的每个文件中,而是作为一系列表单。如果您的流产生的不是一个而是多个s-exps,读者将一次读取一个。

  

快速轻巧,特别是如果它被调用了几次;   每个AST只有几KB,但它们可以加起来几百MB

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

  

至少应该在Linux上工作;跨平台会很好

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

答案 1 :(得分:0)

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

我试了一下球拍,感到惊喜;它比我之前使用的其他Lisps(Emacs Lisp和各种特定于应用程序的Scheme脚本)更好,因为它有很好的文档和电池包括标准库。

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

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

我发现Racket的启动时间非常慢,所以如果速度是一个问题,我不建议一遍又一遍地调用脚本作为循环的一部分。很容易将我的循环移到Racket中,然后调用一次脚本。