如何在捕获输出时在 Crystal 中执行 shell 脚本?
How to execute a shell script in Crystal while capturing output?
我想在处理 stdout 和 stderr 输出时执行 shell 脚本。目前,我使用 Process.run
、shell=false
和三个用于 stdin、stdout 和 stderr 的管道来执行命令。我生成纤程以从 stdout 和 stderr 读取并记录(或以其他方式处理)输出。这对于单个命令非常有效,但对于脚本却非常失败。
我可以在调用 Process.run
时简单地设置 shell=true
,但查看 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`
如果我改用 ``
反引号执行方法,它可能会起作用;但这样我就无法实时捕获输出。
我的理解是基于阅读 run.cr
文件的源代码。该行为在处理命令和参数方面与其他语言非常相似。
没有 shell=true
,Process.run
的默认行为是使用命令作为 可执行文件 到 运行。这意味着该字符串需要是一个程序名称,不带任何参数,例如uname
是一个有效的名称,因为我的系统上有一个名为 uname
的程序,位于 /usr/bin
.
如果您曾经成功地将 %{bash -c "echo hello world"}
与 shell=false
一起使用,那么一定有问题 - 默认行为应该是尝试 运行 一个名为 [=19] 的程序=],这不太可能存在于任何系统上。
一旦你传入 '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, …)
请注意,我在这里使用了一个参数数组 - 如果没有参数数组,您将无法控制参数如何传递到 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.
问题是 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)
或者如果你想调用 shell 脚本并获得我刚刚尝试使用 crystal 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
我想在处理 stdout 和 stderr 输出时执行 shell 脚本。目前,我使用 Process.run
、shell=false
和三个用于 stdin、stdout 和 stderr 的管道来执行命令。我生成纤程以从 stdout 和 stderr 读取并记录(或以其他方式处理)输出。这对于单个命令非常有效,但对于脚本却非常失败。
我可以在调用 Process.run
时简单地设置 shell=true
,但查看 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`
如果我改用 ``
反引号执行方法,它可能会起作用;但这样我就无法实时捕获输出。
我的理解是基于阅读 run.cr
文件的源代码。该行为在处理命令和参数方面与其他语言非常相似。
没有 shell=true
,Process.run
的默认行为是使用命令作为 可执行文件 到 运行。这意味着该字符串需要是一个程序名称,不带任何参数,例如uname
是一个有效的名称,因为我的系统上有一个名为 uname
的程序,位于 /usr/bin
.
如果您曾经成功地将 %{bash -c "echo hello world"}
与 shell=false
一起使用,那么一定有问题 - 默认行为应该是尝试 运行 一个名为 [=19] 的程序=],这不太可能存在于任何系统上。
一旦你传入 '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, …)
请注意,我在这里使用了一个参数数组 - 如果没有参数数组,您将无法控制参数如何传递到 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.
问题是 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)
或者如果你想调用 shell 脚本并获得我刚刚尝试使用 crystal 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