我想从 Rakefile执行一些 bash命令。
Rakefile
bash
我在我的 Rakefile中尝试了以下方法
task :hello do %{echo "World!"} end
但是在执行 rake hello时没有输出? 如何从 Rakefile 执行 bash 命令?
rake hello
注意 : 这不是一个副本,因为它专门询问如何从 Rakefile执行 bash 命令。
在 Ruby 中有几种执行 shell 命令的方法。一个简单的(可能也是最常见的)方法是使用反勾:
task :hello do `echo "World!"` end
在 shell 命令的标准输出成为返回值的地方,反勾有一个很好的效果。因此,例如,您可以通过执行
shell_dir_listing = `ls`
但是还有许多其他方法可以调用 shell 命令,它们都有优缺点,工作方式也不同。这篇文章详细解释了这些选择,但下面是一个简短的概括:
Stdout =% x { cmd } -幕后替换反勾的语法 它在做同样的事情
Exec (cmd) -用一个新的 cmd 进程完全替换正在运行的进程
Success = system (cmd) -运行一个子进程并返回 true/false 关于成功/失败(基于 cmd 退出状态)
IO # popen (cmd){ | IO | } -运行一个子进程并连接 stdout 和 标准交易代码
Stdin,stdout,stderr = Open3.popen3(cmd) -运行一个子进程和 连接所有管道(in,out,err)
%{echo "World!"}定义了一个字符串。
%{echo "World!"}
%x{echo "World!"}执行命令并返回输出(stdout)。您将看不到结果。但您可以看到:
%x{echo "World!"}
puts %x{echo "World!"}
调用系统命令的方法有很多:
system( cmd )
popen
Open3#popen3
我认为 rake 希望这种情况发生的方式是: < a href = “ http://rubydoc.info/gems/rake/FileUtils # sh-instance _ method”> http://rubydoc.info/gems/rake/fileutils#sh-instance_method 例如:
task :test do sh "ls" end
内置的 rake 函数 sh 负责处理命令的返回值(如果命令的返回值不是0,则任务将失败) ,此外它还输出命令的输出。
考虑到一致意见似乎更喜欢 rake 的 #sh方法,但 OP 显式地请求 bash,这个答案可能有一些用处。
#sh
这是相关的,因为 Rake#sh使用 Kernel#system调用来运行 shell 命令。Ruby 硬编码到 /bin/sh,忽略用户在环境中配置的 shell 或 $SHELL。
Rake#sh
Kernel#system
/bin/sh
$SHELL
下面是一个从 /bin/sh调用 bash 的解决方案,它允许您仍然使用 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是从铁路公司借来的:
#strip_heredoc
Https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb
您可以通过需要 active _ support 来获得它,或者当您处于 Rail 项目中时,它可能会自动加载,但是我使用的是外部 Rail,因此必须自己定义它。
有两个异端,一个外部的标记 EOS和一个内部的标记 BASH。
EOS
BASH
这种工作方式是将 BASH 标记之间的内部 herdoc 提供给 BASH 的 stdin。注意,它是在 /bin/sh的上下文中运行的,所以它是 posx heredoc,而不是 Ruby。通常,这要求结束标记位于列1中,这里不是这种情况,因为这里有缩进。
然而,因为它被包装在一个红宝石的 herdoc 中,所以应用在那里的 strip_heredoc方法去缩进它,在 /bin/sh看到它之前,将内部 herdoc 的左侧的全部内容放置在第1列中。
strip_heredoc
/bin/sh通常还会在 herdoc 中展开变量,这可能会干扰脚本。起始标记‘ BASH’周围的单引号告诉 /bin/sh在传递给 BASH 之前不要在 herdoc 中展开任何内容。
但是,在将字符串传递给 bash 之前,/bin/sh仍然对字符串应用转义。这意味着反斜杠转义必须加倍才能通过 /bin/sh到 bash,也就是说 \变成 \\。
\
\\
Bash 选项 -xeu是可选的。
-xeu
-eu参数告诉 bash 在严格模式下运行,这将在任何失败或引用未定义变量时停止执行。这将向 rake 返回一个错误,从而停止 rake 任务。通常,这是你想要的。如果需要正常的 bash 行为,则可以删除参数。
-eu
Bash 的 -x选项和 #sh的 {verbose: false}参数协同工作,这样 rake 只打印实际执行的 bash 命令。如果 bash 脚本并不打算完整地运行,例如,如果它有一个测试,允许它在脚本的早期优雅地退出,那么这将非常有用。
-x
{verbose: false}
如果您不希望 rake 任务失败,请注意不要设置除0以外的退出代码。通常,这意味着您不希望在没有显式设置退出代码(即 || exit 0)的情况下使用任何 || exit构造。
|| exit 0
|| exit
有两种方式:
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)会死亡。
sh "expr"
sh "irb"
%x(irb)