6

我想在处理 stdout 和 stderr 输出时执行一个 shell 脚本。Process.run目前,我使用标准输入、shell=false标准输出和标准错误的三个管道执行命令。我生成纤程以从 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水晶主”>/etc/apt/sources.list.d/crystal.list`

如果我改用``反引号执行方法,它可能会起作用;但是我将无法实时捕获输出。

4

3 回答 3

12

问题是 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)
于 2016-02-18T19:39:54.260 回答
5

I'm basing my understanding on reading the source code of the run.cr file. The behaviour is very similar to other languages in how it deals with commands and arguments.

Without shell=true, the default behaviour of Process.run is to use the command as the executable to run. This means that the string needs to be a program name, without any arguments, e.g. uname would be a valid name as there's a program on my system called uname in /usr/bin.

If you ever got behaviour of successfully using %{bash -c "echo hello world"} with shell=false, then something is wrong - the default behaviour should have been to try to run a program called bash -c "echo hello world", which is unlikely to exist on any system.

Once you pass in 'shell=true', then it does sh -c <command>, which will allow strings like echo hello world as a command to work; this will also allow redirections and pipelines to work.

The shell=true behaviour can generally be interpreted as doing the following:

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

Note that I'm using an array of arguments here - without the array of arguments, you don't have any control over how the arguments are passed into the shell.

The reason why the first version, with or without shell=true doesn't work is because the pipeline is outside the -c, which is the command you're sending to bash.

于 2016-02-18T17:20:07.557 回答
2

或者,如果您想调用 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
于 2017-10-12T14:41:42.477 回答