从Rakefile执行bash命令

rud*_*ph9 66 ruby bash rake

我想bash从一个执行一些命令Rakefile.

我在我的尝试中尝试了以下内容 Rakefile

task :hello do
  %{echo "World!"}
end
Run Code Online (Sandbox Code Playgroud)

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

注意:这不是重复,因为它具体询问如何从Rakefile执行bash命令.

Giz*_*wai 121

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

task :test do
  sh "ls"
end
Run Code Online (Sandbox Code Playgroud)

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

  • 这应该是公认的答案,问题明确提到了瑞克 (9认同)
  • 像反引号执行等其他的解决方案是没有佣金的方法,但是Ruby方法(见http://zhangxh.net/programming/ruby/6-ways-to-run-shell-commands-in-ruby/ 6不同方法).rake在"sh"函数中做的是自动处理命令的返回值,这样如果sh中的一个命令失败,rake任务就会失败.大部分时间这都是你想要的.从ruby执行命令的其他方法不应该经常用于rake. (7认同)
  • sh over system的另一个好处:默认情况下,sh似乎对echo'ing命令更加冗长. (4认同)
  • 我在rake文件而不是系统中使用sh取得了最大的成功. (3认同)
  • 鉴于RubyForge已不复存在,截至2014年7月这个链接已经死了. (3认同)

Ben*_*Lee 58

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

task :hello do
  `echo "World!"`
end
Run Code Online (Sandbox Code Playgroud)

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

shell_dir_listing = `ls`
Run Code Online (Sandbox Code Playgroud)

但是还有许多其他方法可以调用shell命令,它们都有优点/缺点并且工作方式不同.本文详细解释了这些选择,但这里有一个快速概述的可能性:


knu*_*nut 5

%{echo "World!"}定义一个字符串。我希望你想要%x{echo "World!"}

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

puts %x{echo "World!"}
Run Code Online (Sandbox Code Playgroud)

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

  • 反引号:`
  • system( cmd )
  • popen
  • Open3#popen3


Bin*_*ile 5

鉴于共识似乎更喜欢rake的#sh方法,但是OP明确要求使用bash,因此此答案可能会有用。

这很重要,因为Rake#sh使用Kernel#system调用来运行shell命令。Ruby将其硬编码为/bin/sh,而忽略了用户配置的外壳程序或$SHELL环境中的内容。

这是一种从调用bash的解决方法/bin/sh,使您仍然可以使用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
Run Code Online (Sandbox Code Playgroud)

#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的stdin。请注意,它是在的上下文中运行的/bin/sh,因此它是posix heredoc,而不是红宝石。通常,这要求将结束标记放在第1列中,由于缩进,此处不是这种情况。

但是,由于它被包裹在红宝石heredoc中,因此strip_heredoc在此处应用的方法会使其缩痕,将内部heredoc的整个左侧放置在第1列中,然后再进行/bin/sh查看。

/bin/sh通常也会在heredoc中扩展变量,这可能会干扰脚本。起始标记“ BASH”周围的单引号指示/bin/sh在传递给bash之前,请勿在heredoc中进行任何扩展。

但是/bin/sh,在将字符串传递给bash之前,仍会对字符串应用转义。这意味着反斜杠转义必须加倍以使其/bin/sh达到bash,即\成为\\

bash选项-xeuo pipefail是可选的。

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

-xbash 的选项和{verbose: false}参数可以#sh协同工作,以便rake仅打印实际执行的bash命令。如果您的bash脚本不是要完全运行,例如,如果它具有允许其在脚本中提前退出的测试,则这很有用。

如果您不希望rake任务失败,请注意不要设置非0的退出代码。通常,这意味着您|| exit无需显式设置退出代码即不想使用任何构造|| exit 0

如果您在此过程中遇到任何怪异的怪事,请关闭-o pipefail。我已经看到一些与之相关的错误,特别是在管道传输到时grep