我想在处理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`
如果我使用``
反引号执行方法,它可能会有效;但后来我无法实时捕获输出。
答案 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=true
,Process.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