我想从bash
执行一些Rakefile
命令。
我在我的Rakefile
尝试过以下内容
task :hello do
%{echo "World!"}
end
但在执行rake hello
时没有输出?如何从Rakefile执行bash命令?
注意:这不是重复,因为它具体询问如何从Rakefile执行bash命令。
我认为rake希望这种情况发生的方式是:http://rubydoc.info/gems/rake/FileUtils#sh-instance_method示例:
task :test do
sh "ls"
end
内置rake函数sh负责命令的返回值(如果命令的返回值不是0,则任务失败),此外它还输出命令输出。
有几种方法可以在ruby中执行shell命令。一个简单的(可能是最常见的)是使用反引号:
task :hello do
`echo "World!"`
end
反引号具有很好的效果,其中shell命令的标准输出变为返回值。因此,例如,您可以通过执行来获取ls
的输出
shell_dir_listing = `ls`
但是还有许多其他方法可以调用shell命令,它们都有优点/缺点并且工作方式不同。 This article详细解释了这些选择,但这里有一个快速概括的可能性:
鉴于共识似乎更喜欢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
时看到了一些与之相关的问题。
%{echo "World!"}
定义了一个String。我希望你想要%x{echo "World!"}
。
%x{echo "World!"}
执行命令并返回输出(stdout)。你不会看到结果。但你可能会这样做:
puts %x{echo "World!"}
有更多方法可以调用系统命令:
system( cmd )
popen
Open3#popen3
有两种方法:
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)
将会死亡。