鉴于共识似乎更喜欢 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 是从 rails 借来的:
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb
您可以通过要求 active_support 来获得它,或者当您在 rails 项目中时它可能会自动加载,但我使用的是外部 rails,因此必须自己定义它。
有两个 heredocs,一个带有标记 EOS 的外部文档和一个带有标记 BASH 的内部文档。
它的工作方式是将 BASH 标记之间的内部 heredoc 提供给 bash 的标准输入。请注意,它是在/bin/sh 的上下文中运行的,所以它是一个posix heredoc,而不是一个ruby。通常这要求结束标记位于第 1 列中,但由于缩进,此处不是这种情况。
但是,因为它被包裹在一个 ruby heredoc 中,所以在此处应用的 strip_heredoc 方法会对其进行去缩进,将内部 heredoc 的整个左侧放在第 1 列中,然后 /bin/sh 看到它。
/bin/sh 通常也会扩展heredoc 中的变量,这可能会干扰脚本。开始标记“BASH”周围的单引号告诉/bin/sh 在将heredoc 传递给bash 之前不要扩展任何内容。
不过,/bin/sh 在将字符串传递给 bash 之前,仍然会对字符串应用转义。这意味着反斜杠转义必须加倍才能通过/bin/sh 到bash,即\ 变为\\。
bash 选项-xeu 是可选的。
-eu 参数告诉 bash 在严格模式下运行,这会在任何失败或对未定义变量的引用时停止执行。这将向 rake 返回一个错误,这将停止 rake 任务。通常,这就是您想要的。如果您想要正常的 bash 行为,可以删除参数。
bash 的-x 选项和#sh 的{verbose: false} 参数协同工作,因此 rake 只打印实际执行的 bash 命令。如果您的 bash 脚本不打算完整运行,这很有用,例如,如果它有一个测试允许它在脚本早期正常退出。
如果您不希望 rake 任务失败,请注意不要设置 0 以外的退出代码。通常,这意味着您不想在不明确设置退出代码的情况下使用任何 || exit 构造,即 || exit 0。