从Rakefile执行bash命令


76

我想从中执行许多bash命令Rakefile

我已经尝试了以下内容 Rakefile

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

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

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


不是%{,而是,它%x(以字符串形式返回stdout而不是打印它。
Ciro Santilli郝海东冠状病六四事件法轮功

8
如上所述,它不是重复项,因为它专门要求在rake文件中而不是在常规的ruby程序中执行。
Gizmomogwai

当它专门询问Rake而不是一般的Ruby时,为什么将其标记为重复项?
silverdr

Answers:


131

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

task :test do
  sh "ls"
end

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


7
其他解决方案,例如用反引号执行等等,不是rake方法,而是ruby方法(请参阅zhangxh.net/programming/ruby/…了解6种不同方式)。rake在“ sh”函数中的作用是自动处理命令的返回值,因此,如果sh中的一个命令失败,则rake任务也会失败。大多数时候,这就是您想要的。从红宝石执行命令的其他方式不应该经常从rake中使用。
Gizmomogwai

4
sh over system的另一个好处:默认情况下,sh似乎对echo'ing命令更为详细。
路加福音

4
在rake文件而不是system中使用sh取得了最大的成功。
orkoden 2013年

3
鉴于RubyForge不再存在,该链接已于2014年7月终止。
Arto Bendiken 2014年

1
@ArtoBendiken更新了评论,以包括指向rdoc的最新链接。
Gizmomogwai 2014年

59

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

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

在shell命令的标准输出成为返回值的地方,反引号具有很好的效果。因此,例如,您可以ls通过执行以下操作获取输出

shell_dir_listing = `ls`

但是还有许多其他方法可以调用shell命令,它们都有好处/缺点,并且工作方式也不同。本文详细介绍了这些选择,但是这里提供了一个简要的摘要:


这确实会将输出记录到控制台,这可能会有些混乱。在rake中,您可以sh "some_task"将命令打印到终端,就像您直接从终端运行命令一样。
Automatico

7

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

但是,由于它包裹在红宝石Heredoc中,因此strip_heredoc此处应用的方法会使其缩进,在/bin/sh看到之前将内部Heredoc的整个左侧放置在第1列中。

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

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

bash选项-xeu是可选的。

-eu参数告诉bash在严格模式下,在任何故障或参考,其停止执行到未定义的变量运行。这将向rake返回错误,这将停止rake任务。通常,这就是您想要的。如果您想要正常的bash行为,则可以删除该参数。

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

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


1
谢谢。引导正确的方向对我有帮助。我在ruby脚本中使用它,需要调用一些需要bash的方法。我还使用带有一些“奇怪”字符的长字符串。这是我的方法,也许它可以帮助某人。 system(%[xx=$(cat <<EOT\n#{body_text}\nEOT\n); bash <<BASH\n. #{ENV['HOME']}/.bash_profile; echo "$xx" | own_mail -TRm\nBASH\n)]。但是,最简单的方法是将所有内容放入bash脚本中,然后调用它system("path/my_bash_script.sh")
244an

5

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

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

puts %x{echo "World!"}

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

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

1
应当注意,%x反引号实际上是在调用等效的ruby内核方法,它们是相同精确方法的替代语法。
本·李

1
system( cmd )为我工作。我试图使用wkhtmltopdf编写pdf生成器脚本。
tsega

1

有两种方法:

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外壳,而%x(irb)会死掉。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.