从Rakefile执行bash命令

时间:2012-03-20 22:33:15

标签: ruby bash rake

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

我在Rakefile

中尝试过以下操作
task :hello do
  %{echo "World!"}
end

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

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

5 个答案:

答案 0 :(得分:117)

我认为rake希望这种情况发生的方式是:http://rubydoc.info/gems/rake/FileUtils#sh-instance_method 例如:

task :test do
  sh "ls"
end

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

答案 1 :(得分:57)

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

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

反引号具有很好的效果,其中shell命令的标准输出变为返回值。因此,例如,您可以通过执行

获取ls的输出
shell_dir_listing = `ls`

但是还有许多其他方法可以调用shell命令,它们都有好处/缺点,而且工作方式不同。 This article详细解释了这些选择,但这里有一个快速概括的可能性:

答案 2 :(得分:4)

鉴于共识似乎更喜欢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 -xeuo pipefail <<'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项目中时它可能是自动加载的,但是我在外面使用这个轨道,所以不得不自己定义它。

有两个heredocs,一个带有标记EOS的外部heredocs和一个带有标记BASH的内部heredocs。

这种方法的工作方式是将BASH标记之间的内部heredoc提供给bash的stdin。请注意,它在/bin/sh的上下文中运行,所以它是一个posix heredoc而不是ruby。通常,这需要结束标记位于第1列,由于缩进,这不是这种情况。

但是,因为它被包裹在ruby heredoc中,所以应用strip_heredoc方法将其置于其中,在/bin/sh看到它之前将内部heredoc的左侧全部放在第1列中。 / p>

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

但是/bin/sh在将字符串传递给bash之前仍然会对字符串应用转义。这意味着反斜杠转义必须加倍才能通过/bin/sh进行重击,即\变为\\

bash选项-xeuo pipefail是可选的。

-euo pipefail参数告诉bash以严格模式运行,它会在任何失败或引用未定义变量时停止执行,甚至是管道中的命令。这将向rake返回一个错误,这将停止rake任务。通常这就是你想要的。如果你想要正常的bash行为,可以删除这些参数。

bash的-x选项和{verbose: false}的{​​{1}}参数协同工作,以便rake只打印实际执行的bash命令。如果您的bash脚本不是要完整运行,那么这很有用,例如,如果它有一个允许它在脚本的早期正常退出的测试。

如果您不希望rake任务失败,请注意不要设置0以外的退出代码。通常这意味着您不希望在未明确设置退出代码的情况下使用任何#sh构造,即|| exit

如果您遇到任何bash怪异,请关闭|| exit 0。在流水线化-o pipefail时,我已经看到了与它有关的一些麻烦。

答案 3 :(得分:3)

%{echo "World!"}定义了一个String。我希望你想要%x{echo "World!"}

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

puts %x{echo "World!"}

有更多方法可以调用系统命令:

  • 反叛:“
  • system( cmd )
  • popen
  • Open3#popen3

答案 4 :(得分:0)

有两种方法:

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)将死亡。