2015-04-01 19 views
-1

假设我有这样一个字符串以下解析壳报价串入execv兼容参数向量

echo "foo" "bar\"blub""baz" "'" "\"" foo\ bar "\\" '\'' """"  Lots" "of\ whitespace 

现在我想执行上面的字符串为命令,如果它是通过对Unix.execv呼叫键入到外壳。如果我没有犯任何错误,那么外壳将解析上述分为以下ocaml的名单:

["echo"; "foo"; "bar\"blubbaz"; "'"; "\""; "foo bar"; "\\"; "'", "", "Lots of whitespace"] 

哪个库让我从原来的字符串来解析名单?

最终我想把结果列表交给Unix.execvpe。还有Unix.open_process_full,它能够通过使用/bin/sh来处理我的原始字符串,但是我发现在直接调用外部程序时没有使用/bin/sh,我的应用程序速度提高了16%。现在我想能够接受更多的输入字符串,包括引用和转义。

我必须推出自己的解析器吗?

存在POSIX函数wordexp但包装该函数不会解决我的问题,因为wordexp比我想要的更多(命令替换,计算globs,替换tilda和环境变量)。

我只想要引用和转义解决。

+0

我不知道标准(如“POSIX或...”标准)的功能,可以完成您想要的工作。这意味着你很可能最终会滚动你自己的,或者挖掘一个或多个shell的代码等。 – 2015-04-01 22:37:57

+0

为什么你想要将类似但不完全像shell的词分开?你如何确定你想要支持哪些语法功能,以及哪些不支持?为什么'sh',而不是一个更好的引用格式? – 2015-04-02 05:40:25

+0

@thatotherguy输入来自一个配置文件,该文件存储要在变量中执行的字符串。该字符串不应该包含通配符,环境变量或tilda。如果我使用'wordexp',那么我不能告诉它抛出错误或以其他方式拒绝解析包含这些东西的字符串。你问为什么不比POSIX shell更好的引用格式? – josch 2015-04-02 06:44:37

回答

0

我用ocamllex熟练解决了这个问题。张贴在这里以防别人想要做类似的事情。它应该很容易扩展,以包含当前支持的转义字符和其他shell功能范围之外的功能。

{ 
    exception UnknownShellEscape of string 
    exception UnmatchedChar of char 
    let buf_from_str str = 
    let buf = Buffer.create 16 in 
    Buffer.add_string buf str; 
    buf 
} 

let safechars = [^ '"' ''' '\\' ' ' '\t']+ 
let space = [ ' ' '\t' ]+ 

rule shell_command argv = parse 
| space   { shell_command argv lexbuf } 
| safechars  { uquote argv (buf_from_str (Lexing.lexeme lexbuf)) lexbuf } 
| '\\' '"'  { uquote argv (buf_from_str "\"") lexbuf } 
| '\\' '''  { uquote argv (buf_from_str "'") lexbuf } 
| '\\' '\\'  { uquote argv (buf_from_str "\\") lexbuf } 
| '\\' ' '  { uquote argv (buf_from_str " ") lexbuf } 
| '\\' _ as c { raise (UnknownShellEscape c) } 
| '"'   { dquote argv (Buffer.create 16) lexbuf } 
| '''   { squote argv (Buffer.create 16) lexbuf } 
| _ as c  { raise (UnmatchedChar c) } 
| eof { List.rev argv } 
and uquote argv buf = parse 
| (space|eof) { shell_command ((Buffer.contents buf)::argv) lexbuf } 
| '\\' '"' { Buffer.add_string buf "\""; uquote argv buf lexbuf } 
| '\\' ''' { Buffer.add_string buf "'"; uquote argv buf lexbuf } 
| '\\' '\\' { Buffer.add_string buf "\\"; uquote argv buf lexbuf } 
| '\\' ' ' { Buffer.add_string buf " "; uquote argv buf lexbuf } 
| '\\' _ as c { raise (UnknownShellEscape c) } 
| '"'   { dquote argv buf lexbuf } 
| '''   { squote argv buf lexbuf } 
| safechars { Buffer.add_string buf (Lexing.lexeme lexbuf); uquote argv buf lexbuf } 
| _ as c  { raise (UnmatchedChar c) } 
and dquote argv buf = parse 
| '"' (space|eof) { shell_command ((Buffer.contents buf)::argv) lexbuf } 
| '"' '"'   { dquote argv buf lexbuf } 
| '"' '''   { squote argv buf lexbuf } 
| '"'    { uquote argv buf lexbuf } 
| '\\' '"'  { Buffer.add_string buf "\""; dquote argv buf lexbuf } 
| '\\' '\\'  { Buffer.add_string buf "\\"; dquote argv buf lexbuf } 
| '\\' _ as c  { raise (UnknownShellEscape c) } 
| [^ '"' '\\' ]+ { Buffer.add_string buf (Lexing.lexeme lexbuf); dquote argv buf lexbuf } 
| _ as c   { raise (UnmatchedChar c) } 
and squote argv buf = parse 
| ''' (space|eof) { shell_command ((Buffer.contents buf)::argv) lexbuf } 
| ''' '''   { squote argv buf lexbuf } 
| ''' '"'   { dquote argv buf lexbuf } 
| '''    { uquote argv buf lexbuf } 
| [^ ''' ]+  { Buffer.add_string buf (Lexing.lexeme lexbuf); squote argv buf lexbuf } 
| _ as c   { raise (UnmatchedChar c) } 

{ 
    let main() = 
    let cin = 
     if Array.length Sys.argv > 1 
     then open_in Sys.argv.(1) 
     else stdin 
    in 
    let lexbuf = Lexing.from_channel cin in 
    let argv = shell_command [] lexbuf in 
    List.iter (Printf.printf "%s\n") argv 

    let _ = Printexc.print main() 
} 

要尝试一下运行:

$ ocamllex test.mll 
$ echo 'echo "foo" "bar\\"blub""baz" "'\''" "\\"" foo\\ bar '\ 
> '"\\\\" """"'\'''\'''\'''\''""  Lots" "of\\ whitespace' \ 
> | ocaml test.ml 
echo 
foo 
bar"blubbaz 
' 
" 
foo bar 
\ 

Lots of whitespace 

成功! \ o/