从Rakefile执行bash命令

问题描述 投票:64回答:5

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

我在我的Rakefile尝试过以下内容

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

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

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

ruby bash rake
5个回答
117
投票

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

task :test do
  sh "ls"
end

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


57
投票

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

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

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

shell_dir_listing = `ls`

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


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的外部和一个带有标记BASH的内部。

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

然而,因为它被包裹在红宝石heredoc中,所以strip_heredoc方法在那里应用了它,在/bin/sh看到它之前将内部heredoc的左侧整体放置在第1列。

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

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

bash选项-xeuo pipefail是可选的。

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

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

如果您不希望rake任务失败,请注意不要设置0以外的退出代码。通常这意味着你不想使用任何|| exit结构而不明确设置退出代码,即|| exit 0

如果你遇到任何bash古怪,请关闭-o pipefail。我特意在管道到grep时看到了一些与之相关的问题。


3
投票

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

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

puts %x{echo "World!"}

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

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

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,但是stdout不会显示在屏幕上。所以,当你需要命令结果时,最好使用%x( expr )

但是如果你只是想做一个shell命令,我建议你使用sh "expr"。因为sh "irb"会让你进入irb shell,而%x(irb)将会死亡。

© www.soinside.com 2019 - 2024. All rights reserved.