从Rakefile执行bash命令
我想从Rakefile
执行一些bash
命令。 我的主动shell是bash
,我将从bash
调用rake
。
我在我的Rakefile
包含了以下内容
task :hello do %{echo "World!"} end
但执行rake hello
没有输出?
如何从Rakefile执行bash命令?
注 :这不是重复的,因为它具体询问如何从Rakefile执行bash命令。
我认为耙子想要发生的方式是: 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命令,它们都有好处/缺点,工作方式也不同。 这篇文章详细解释了select,但是这里有一个简单的总结可能性:
-
stdout =%x {cmd} – 反引号的替代语法,在幕后它做同样的事情
-
exec(cmd) – 用新的cmd进程完全replace正在运行的进程
-
成功=系统(CMD) – 运行一个subprocess,并成功/失败返回true / false(基于cmd退出状态)
-
IO#popen(cmd){| io | } – 运行一个subprocess,将stdout和stderr连接到io
-
stdin,stdout,stderr = Open3.popen3(cmd) – 运行一个subprocess并连接到所有pipe道(in,out,err)
%{echo "World!"}
定义了一个String。 我希望你想要%x{echo "World!"}
。
%x{echo "World!"}
执行命令并返回输出(stdout)。 你不会看到结果。 但是你可以这样做:
puts %x{echo "World!"}
有更多的方法来调用系统命令:
- 反衬:`
-
system( cmd )
-
popen
-
Open3#popen3
鉴于共识似乎更喜欢rake的#sh
方法,但OP明确地要求bash,这个答案可能有一些用处。
这是相关的,因为Rake#sh
使用Kernel#system
调用来运行shell命令。 Ruby将其硬编码到/bin/sh
,忽略用户在环境中configuration的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
从轨道借用:
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的标准input。 请注意,它运行在/bin/sh
的上下文中,所以这是一个posix heredoc不是一个ruby。 通常情况下,需要将结束标记放在第1列中,这是因为缩进而不是这种情况。
然而,因为它被包装在ruby heredoc中,所以在strip_heredoc
应用的strip_heredoc
方法取消了它,在/bin/sh
之前将内部heredoc的左侧整体放在第一列中。
/bin/sh
通常也会在heredoc中扩展variables,这可能会干扰脚本。 围绕开始标记“BASH”的单引号告诉/bin/sh
在传递给bash之前,不要在heredoc内部展开任何内容。
然而/bin/sh
在把它传递给bash之前仍然对string应用转义。 这意味着反斜杠转义必须加倍,以通过/bin/sh
bash,即\
成为\\
。
bash选项-xeuo pipefail
是可选的。
-euo pipefail
参数告诉bash在严格模式下运行,当任何失败或引用一个未定义的variables,甚至是pipe道中的命令时,都会停止执行。 这将返回一个错误耙,这将停止耙任务。 通常这是你想要的。 如果你想要正常的bash行为,参数可以被删除。
#sh
的-x
选项和{verbose: false}
参数#sh
工作,所以rake只打印实际执行的bash命令。 如果您的bash脚本不是完全运行的,例如它有一个允许它在脚本中早期退出的testing,这是非常有用的。
如果您不希望rake任务失败,请小心不要设置0以外的退出代码。 通常这意味着你不想使用任何|| exit
|| exit
构造没有显式设置退出代码,即|| exit 0
|| exit 0
。
如果你遇到任何bash怪异的情况,关掉-o pipefail
。 我已经看到了与stream水线grep
有关的一些bugginess。