ruby 从 Rakefile 执行 bash 命令

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9796028/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:55:17  来源:igfitidea点击:

Execute bash commands from a Rakefile

rubybashrake

提问by rudolph9

I would like to execute a number of bashcommands from a Rakefile.

我想bashRakefile.

I have tried the following in my Rakefile

我在我的 Rakefile

task :hello do
  %{echo "World!"}
end

but upon executing rake hellothere is no output? How do I execute bash commands from a Rakefile?

但是在执行rake hello时没有输出?如何从 Rakefile 执行 bash 命令?

NOTE:This is not a duplicate as it's specifically asking how to execute bash commands from a Rakefile.

注意:这不是重复的,因为它专门询问如何从Rakefile执行 bash 命令。

回答by Gizmomogwai

I think the way rake wants this to happen is with: http://rubydoc.info/gems/rake/FileUtils#sh-instance_methodExample:

我认为 rake 希望这种情况发生的方式是:http: //rubydoc.info/gems/rake/FileUtils#sh-instance_method示例:

task :test do
  sh "ls"
end

The built-in rake function sh takes care of the return value of the command (the task fails if the command has a return value other than 0) and in addition it also outputs the commands output.

内置的 rake 函数 sh 负责命令的返回值(如果命令的返回值不是 0,则任务失败),此外它还输出命令输出。

回答by Ben Lee

There are several ways to execute shell commands in ruby. A simple one (and probably the most common) is to use backticks:

在 ruby​​ 中有几种执行 shell 命令的方法。一个简单的(可能也是最常见的)是使用反引号:

task :hello do
  `echo "World!"`
end

Backticks have a nice effect where the standard output of the shell command becomes the return value. So, for example, you can get the output of lsby doing

反引号有很好的效果,shell 命令的标准输出成为返回值。因此,例如,您可以ls通过执行以下操作获得输出

shell_dir_listing = `ls`

But there are many other ways to call shell commands and they all have benefits/drawbacks and work differently. This articleexplains the choices in detail, but here's a quick summary possibilities:

但是还有许多其他方法可以调用 shell 命令,它们都有优点/缺点并且工作方式不同。本文详细解释了这些选择,但这里有一个快速总结的可能性:

回答by Binary Phile

Given that the consensus seems to prefer rake's #shmethod, but OP explicitly requests bash, this answer may have some use.

鉴于共识似乎更喜欢 rake 的#sh方法,但 OP 明确要求 bash,这个答案可能有一些用处。

This is relevant since Rake#shuses the Kernel#systemcall to run shell commands. Ruby hardcodes that to /bin/sh, ignoring the user's configured shell or $SHELLin the environment.

这是相关的,因为Rake#sh使用Kernel#system调用来运行 shell 命令。Ruby 将其硬编码为/bin/sh,忽略用户配置的 shell 或$SHELL环境。

Here's a workaround which invokes bash from /bin/sh, allowing you to still use the shmethod:

这是一个从 调用 bash 的解决方法/bin/sh,允许您仍然使用该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_heredocis borrowed from rails:

#strip_heredoc是从 rails 借来的:

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb

You could probably get it by requiring active_support, or maybe it's autoloaded when you're in a rails project, but I was using this outside rails and so had to def it myself.

您可能可以通过要求 active_support 来获得它,或者当您在 Rails 项目中时它可能会自动加载,但我使用的是外部 rails,因此必须自己定义它。

There are two heredocs, an outer one with the markers EOSand an inner one with the markers BASH.

有两个 heredocs,一个带有标记的外部一个和一个带有标记EOS的内部一个BASH

The way this works is by feeding the inside heredoc between the BASH markers to bash's stdin. Note that it is running within the context of /bin/sh, so it's a posix heredoc not a ruby one. Normally that requires the end marker to be in column 1, which isn't the case here because of the indenting.

其工作方式是将 BASH 标记之间的内部 heredoc 提供给 bash 的标准输入。请注意,它在 的上下文中运行/bin/sh,因此它是 posix heredoc 而不是 ruby​​ 。通常,这要求结束标记位于第 1 列中,由于缩进,此处并非如此。

However, because it's wrapped within a ruby heredoc, the strip_heredocmethod applied there deindents it, placing the entirety of the left side of the inner heredoc in column 1 prior to /bin/shseeing it.

然而,因为它被包裹在一个红宝石 heredoc 中,在strip_heredoc那里应用的方法将它去除,在/bin/sh看到它之前将内部 heredoc 的整个左侧放置在第 1 列中。

/bin/shalso would normally expand variables within the heredoc, which could interfere with the script. The single quotes around the start marker, 'BASH', tell /bin/shnot to expand anything inside the heredoc before it is passed to bash.

/bin/sh也通常会在 heredoc 中扩展变量,这可能会干扰脚本。开始标记周围的单引号 'BASH' 告诉/bin/sh在将其传递给 bash 之前不要在heredoc 内扩展任何内容。

However /bin/shdoes still apply escapes to the string before passing it to bash. That means backslash escapes have to be doubled to make it through /bin/shto bash, i.e. \becomes \\.

但是/bin/sh,在将字符串传递给 bash 之前,它仍然对字符串应用转义。这意味着反斜杠转义必须加倍才能通过/bin/shbash,即\变成\\.

The bash options -xeuo pipefailare optional.

bash 选项-xeuo pipefail是可选的。

The -euo pipefailarguments tell bash to run in strict mode, which stops execution upon any failure or reference to an undefined variable, even a command in a pipeline. This will return an error to rake, which will stop the rake task. Usually this is what you want. The arguments can be dropped if you want normal bash behavior.

这些-euo pipefail参数告诉 bash 以严格模式运行,在任何失败或引用未定义变量时停止执行,甚至是管道中的命令。这将向 rake 返回一个错误,这将停止 rake 任务。通常这就是你想要的。如果您想要正常的 bash 行为,可以删除这些参数。

The -xoption to bash and {verbose: false}argument to #shwork in concert so that rake only prints the bash commands which are actually executed. This is useful if your bash script isn't meant to run in its entirety, for example if it has a test which allows it to exit gracefully early in the script.

-x选项bash和{verbose: false}参数#sh是怎样协同工作,使耙只打印其实际执行的是bash命令。如果您的 bash 脚本不打算完整运行,这将很有用,例如,如果它有一个测试,允许它在脚本的早期优雅地退出。

Be careful to not set an exit code other than 0 if you don't want the rake task to fail. Usually that means you don't want to use any || exitconstructs without setting the exit code explicitly, i.e. || exit 0.

如果您不希望 rake 任务失败,请注意不要设置 0 以外的退出代码。通常这意味着您不想在|| exit没有明确设置退出代码的情况下使用任何构造,即|| exit 0.

If you run into any bash weirdness along the way, turn off the -o pipefail. I've seen a bit of bugginess related to it specifically when pipelining to grep.

如果您在此过程中遇到任何 bash 怪癖,请关闭-o pipefail. 在流水线到grep.

回答by knut

%{echo "World!"}defines a String. I expect you wanted %x{echo "World!"}.

%{echo "World!"}定义一个字符串。我希望你想要%x{echo "World!"}

%x{echo "World!"}executes the command and returns the output (stdout). You will not see the result. But you may do:

%x{echo "World!"}执行命令并返回输出 (stdout)。你不会看到结果。但你可以这样做:

puts %x{echo "World!"}

There are more ways to call a system command:

还有更多调用系统命令的方法:

  • Backticks: `
  • system( cmd )
  • popen
  • Open3#popen3
  • 反引号:`
  • system( cmd )
  • popen
  • Open3#popen3

回答by Neo li

There are two ways:

有两种方式:

sh " expr "

or

或者

%x( expr )

Mind that ( expr ) can be { expr } , | expr | or ` expr `

请注意 ( expr ) 可以是 { expr } , | 表达式 | 或`expr`

The difference is, sh "expr" is a ruby method to execute something, and %x( expr ) is the ruby built-in method. The result and action are different. Here is an example

不同之处在于, sh "expr" 是一种执行某些操作的 ruby​​ 方法,而 %x( expr ) 是 ruby​​ 内置方法。结果和行动是不同的。这是一个例子

task :default do
value = sh "echo hello"
puts value
value = %x(echo world)
puts value
end

get:

得到:

hello  # from sh "echo hello"
true   # from puts value
world  # from puts value

You can see that %x( expr )will only do the shell expr but the stdout will not show in the screen. So, you'd better use%x( expr )when you need the command result.

您可以看到它%x( expr )只会执行 shell expr,但标准输出不会显示在屏幕上。所以,最好%x( expr )在需要命令结果的时候使用。

But if you just want to do a shell command, I recommend you use sh "expr". Because sh "irb"will make you go into the irb shell, while %x(irb)will dead.

但是如果你只想做一个 shell 命令,我建议你使用sh "expr". 因为sh "irb"会让你进入irb shell,同时%x(irb)会死。