我想从bash
执行一些Rakefile
命令。
我在Rakefile
task :hello do
%{echo "World!"}
end
但执行rake hello
时没有输出?
如何从Rakefile执行bash命令?
注意:这不是重复,因为它专门询问如何从 Rakefile 执行bash命令。
答案 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详细解释了这些选择,但这里有一个快速概括的可能性:
stdout = %x{cmd} - 幕后反复使用的替代语法 它正在做同样的事情
exec(cmd) - 使用新的cmd流程完全替换正在运行的流程
success = system(cmd) - 运行子进程并返回true / false 成功/失败(基于cmd退出状态)
IO#popen(cmd) { |io| } - 运行子进程并连接stdout和 stderr to io
stdin, stdout, stderr = Open3.popen3(cmd) - 运行子流程和 连接所有管道(in,out,err)
答案 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)
将死亡。