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

Sod*_*hty 4 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 "| 庆典}

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`

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

Jul*_*ier 11

问题是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
Run Code Online (Sandbox Code Playgroud)

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

Process.run("sh", {"-c", command}, output: io)
Run Code Online (Sandbox Code Playgroud)


Pet*_*esh 5

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

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

如果您曾经成功使用%{bash -c "echo hello world"}with shell=false,那么就有问题了——默认行为应该是尝试运行一个名为 的程序bash -c "echo hello world",这在任何系统上都不可能存在。

一旦你传入'shell=true',它就会执行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, …)
Run Code Online (Sandbox Code Playgroud)

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

之所以第一个版本,有或没有shell=true不工作是因为管道是外面-c,这是你要发送到的bash命令。