从 Rakefile 执行 bash 命令

我想从 Rakefile执行一些 bash命令。

我在我的 Rakefile中尝试了以下方法

task :hello do
%{echo "World!"}
end

但是在执行 rake hello时没有输出? 如何从 Rakefile 执行 bash 命令?

注意 : 这不是一个副本,因为它专门询问如何从 Rakefile执行 bash 命令。

47296 次浏览

在 Ruby 中有几种执行 shell 命令的方法。一个简单的(可能也是最常见的)方法是使用反勾:

task :hello do
`echo "World!"`
end

在 shell 命令的标准输出成为返回值的地方,反勾有一个很好的效果。因此,例如,您可以通过执行

shell_dir_listing = `ls`

但是还有许多其他方法可以调用 shell 命令,它们都有优缺点,工作方式也不同。这篇文章详细解释了这些选择,但下面是一个简短的概括:

%{echo "World!"}定义了一个字符串。

%x{echo "World!"}执行命令并返回输出(stdout)。您将看不到结果。但您可以看到:

puts %x{echo "World!"}

调用系统命令的方法有很多:

  • 退票:
  • system( cmd )
  • popen
  • Open3#popen3

我认为 rake 希望这种情况发生的方式是: < a href = “ http://rubydoc.info/gems/rake/FileUtils # sh-instance _ method”> http://rubydoc.info/gems/rake/fileutils#sh-instance_method 例如:

task :test do
sh "ls"
end

内置的 rake 函数 sh 负责处理命令的返回值(如果命令的返回值不是0,则任务将失败) ,此外它还输出命令的输出。

考虑到一致意见似乎更喜欢 rake 的 #sh方法,但 OP 显式地请求 bash,这个答案可能有一些用处。

这是相关的,因为 Rake#sh使用 Kernel#system调用来运行 shell 命令。Ruby 硬编码到 /bin/sh,忽略用户在环境中配置的 shell 或 $SHELL

下面是一个从 /bin/sh调用 bash 的解决方案,它允许您仍然使用 sh方法:

task :hello_world do
sh <<-EOS.strip_heredoc, {verbose: false}
/bin/bash -xeu <<'BASH'
echo "Hello, world!"
BASH
EOS
end


class String
def strip_heredoc
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, ''.freeze)
end
end

#strip_heredoc是从铁路公司借来的:

Https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb

您可以通过需要 active _ support 来获得它,或者当您处于 Rail 项目中时,它可能会自动加载,但是我使用的是外部 Rail,因此必须自己定义它。

有两个异端,一个外部的标记 EOS和一个内部的标记 BASH

这种工作方式是将 BASH 标记之间的内部 herdoc 提供给 BASH 的 stdin。注意,它是在 /bin/sh的上下文中运行的,所以它是 posx heredoc,而不是 Ruby。通常,这要求结束标记位于列1中,这里不是这种情况,因为这里有缩进。

然而,因为它被包装在一个红宝石的 herdoc 中,所以应用在那里的 strip_heredoc方法去缩进它,在 /bin/sh看到它之前,将内部 herdoc 的左侧的全部内容放置在第1列中。

/bin/sh通常还会在 herdoc 中展开变量,这可能会干扰脚本。起始标记‘ BASH’周围的单引号告诉 /bin/sh在传递给 BASH 之前不要在 herdoc 中展开任何内容。

但是,在将字符串传递给 bash 之前,/bin/sh仍然对字符串应用转义。这意味着反斜杠转义必须加倍才能通过 /bin/sh到 bash,也就是说 \变成 \\

Bash 选项 -xeu是可选的。

-eu参数告诉 bash 在严格模式下运行,这将在任何失败或引用未定义变量时停止执行。这将向 rake 返回一个错误,从而停止 rake 任务。通常,这是你想要的。如果需要正常的 bash 行为,则可以删除参数。

Bash 的 -x选项和 #sh{verbose: false}参数协同工作,这样 rake 只打印实际执行的 bash 命令。如果 bash 脚本并不打算完整地运行,例如,如果它有一个测试,允许它在脚本的早期优雅地退出,那么这将非常有用。

如果您不希望 rake 任务失败,请注意不要设置除0以外的退出代码。通常,这意味着您不希望在没有显式设置退出代码(即 || exit 0)的情况下使用任何 || exit构造。

有两种方式:

sh " expr "

或者

%x( expr )

注意(expr)可以是{ expr }、 | expr | 或‘ expr’

区别在于,sh“ expr”是执行某些内容的 Ruby 方法,而% x (expr)是 Ruby 内置方法。结果和行动是不同的。这里有一个例子

task :default do
value = sh "echo hello"
puts value
value = %x(echo world)
puts value
end

获得:

hello  # from sh "echo hello"
true   # from puts value
world  # from puts value

您可以看到,%x( expr )将只执行 shell expr,但标准输出将不会显示在屏幕上。因此,当您需要命令结果时,最好使用 %x( expr )

但是如果您只想执行 shell 命令,我建议您使用 sh "expr"。因为 sh "irb"会让你进入 irb 外壳,而 %x(irb)会死亡。