如何在捕获输出时在Crystal中执行shell脚本?

时间:2016-02-18 14:51:17

标签: linux bash shell crystal-lang

我想在处理stdout和stderr输出时执行shell脚本。目前我使用Process.run执行命令,shell=false和stdin,stdout和stderr三个管道。我生成光纤以从stdout和stderr读取并记录(或以其他方式处理)输出。这对于单个命令非常有效,但对于脚本来说却非常糟糕。

我可以在调用shell=true时简单地设置Process.run,但是看看Crystal源代码似乎只是在命令行上加上“sh”。我试过在“bash”前加上它并没有帮助。

重定向(>file)和管道(例如curl something | bash)之类的内容似乎不适用于Process.run

例如,要下载shell脚本并执行它,我尝试了:

  

cmd =%{bash -c“curl http://dist.crystal-lang.org/apt/setup.sh”|的bash}

     

Process.run(cmd,...)

添加了初始bash,希望它能启用管道运营商。它似乎没有帮助。我也尝试分别执行每个命令:

  

script.split(“\ n”)。reject(/ ^#/,“”).each {Process.run(...)}

但是,当然,当命令使用重定向或管道时,仍然会失败。例如,命令echo "deb http://dist.crystal-lang.org/apt crystal main" >/etc/apt/sources.list.d/crystal.list只输出:

  

“deb http://dist.crystal-lang.org/apt crystal main”> / etc / apt / sources.list.d / crystal.list`

如果我使用``反引号执行方法,它可能会有效;但后来我无法实时捕获输出。

3 个答案:

答案 0 :(得分:10)

问题是UNIX问题。父进程必须能够访问子进程的STDOUT。使用管道,您必须启动一个将运行整个命令的shell进程,包括| bash而不仅仅是curl $URL。在Crystal中,这是:

command = "curl http://dist.crystal-lang.org/apt/setup.sh | bash"
io = MemoryIO.new
Process.run(command, shell: true, output: io)
output = io.to_s

或者如果你想复制Crystal为你做的事情:

Process.run("sh", {"-c", command}, output: io)

答案 1 :(得分:4)

我的理解是基于阅读run.cr文件的源代码。在处理命令和参数方面,其行为与其他语言非常相似。

如果没有shell=trueProcess.run的默认行为是使用该命令作为可执行文件来运行。这意味着字符串需要是一个程序名,没有任何参数,例如uname将是一个有效的名称,因为uname中我的系统上的程序名为/usr/bin

如果您有%{bash -c "echo hello world"}成功使用shell=false的行为,则出现问题 - 默认行为应该是尝试运行名为bash -c "echo hello world"的程序,这不太可能存在于任何系统上。

传入&#39; shell = true&#39;后,它会sh -c <command>,这将允许echo hello world之类的字符串作为命令工作;这也将允许重定向和管道工作。

shell=true行为通常可以解释为执行以下操作:

cmd = "sh"
args = [] of String
args << "-c" << "curl http://dist.crystal-lang.org/apt/setup.sh | bash"
Process.run(cmd, args, …)

请注意,我在这里使用了一个参数数组 - 没有参数数组,您无法控制参数如何传递到shell中。

  

第一个版本(有或没有shell=true无效)的原因是因为管道之外 -c,这是您的命令&# 39;重新发送给bash。

答案 2 :(得分:1)

或者如果你想调用一个shell脚本并获得输出我只是尝试使用水晶0.23.1并且它的工作!

def screen
    output = IO::Memory.new
     Process.run("bash", args: {"lib/bash_scripts/installation.sh"}, output: output)
     output.close
    output.to_s
end