我想从中执行许多bash
命令Rakefile
。
我已经尝试了以下内容 Rakefile
task :hello do
%{echo "World!"}
end
但是执行rake hello
时没有输出?如何从Rakefile执行bash命令?
注意:这不是重复的,因为它专门询问如何从Rakefile执行bash命令。
我想从中执行许多bash
命令Rakefile
。
我已经尝试了以下内容 Rakefile
task :hello do
%{echo "World!"}
end
但是执行rake hello
时没有输出?如何从Rakefile执行bash命令?
注意:这不是重复的,因为它专门询问如何从Rakefile执行bash命令。
Answers:
我认为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命令,它们都有好处/缺点,并且工作方式也不同。本文详细介绍了这些选择,但是这里提供了一个简要的摘要:
stdout =%x {cmd} -反引号的备用语法,在后台执行相同的操作
exec(cmd) -将运行的进程完全替换为新的cmd进程
success = system(cmd) -运行子进程并在成功/失败时返回true / false(基于cmd退出状态)
IO#popen(cmd){| io | } -运行子和连接输出和错误到IO
stdin,stdout,stderr = Open3.popen3(cmd) -运行子进程并连接到所有管道(输入,输出,错误)
sh "some_task"
将命令打印到终端,就像您直接从终端运行命令一样。
鉴于共识似乎更喜欢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行为,则可以删除该参数。
-x
bash的选项和{verbose: false}
参数可以#sh
协同工作,以便rake仅打印实际执行的bash命令。如果您的bash脚本不是要完全运行,例如,如果它具有允许在脚本的早期正常退出的测试,则这很有用。
如果您不希望rake任务失败,请注意不要设置非0的退出代码。通常,这意味着您|| exit
无需显式设置退出代码即不想使用任何构造|| exit 0
。
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")
有两种方法:
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)
会死掉。
%{
,而是,它%x(
以字符串形式返回stdout而不是打印它。