Выполнять команды bash из Rakefile - PullRequest
64 голосов
/ 21 марта 2012

Я хотел бы выполнить ряд bash команд из Rakefile.

Я попробовал следующее в моей Rakefile

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

но при выполнении rake hello нет вывода? Как выполнить команды bash из файла Rakefile?

ПРИМЕЧАНИЕ : Это не дубликат, так как он специально спрашивает, как выполнять команды bash из Rakefile .

Ответы [ 5 ]

117 голосов
/ 16 января 2013

Я думаю, что рейк хочет, чтобы это произошло с: http://rubydoc.info/gems/rake/FileUtils#sh-instance_method Пример:

task :test do
  sh "ls"
end

Встроенная функция rake sh заботится о возвращаемом значении команды (задача не выполняется, если команда имеет возвращаемое значение, отличное от 0), а также выводит вывод команды.

57 голосов
/ 21 марта 2012

Существует несколько способов выполнения команд оболочки в ruby.Самый простой (и, вероятно, самый распространенный) - использовать обратные галочки:

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

Обратные галочки имеют хороший эффект, когда стандартный вывод команды оболочки становится возвращаемым значением.Так, например, вы можете получить вывод ls, выполнив

shell_dir_listing = `ls`

Но есть много других способов вызова команд оболочки, и все они имеют преимущества / недостатки и работают по-разному. В этой статье подробно объясняется выбор, но вот краткий обзор возможностей:

  • stdout =% x {cmd} - Альтернативный синтаксис дляназад, за кулисами происходит то же самое

  • exec (cmd) - полностью заменить запущенный процесс новым процессом cmd

  • success = system (cmd) - Запустить подпроцесс и вернуть true / false в случае успеха / сбоя (в зависимости от состояния выхода cmd)

  • IO # popen (cmd) {| io |} - запустить подпроцесс и подключить stdout и stderr к io

  • stdin, stdout, stderr = Open3.popen3 (cmd) - запустить подпроцесс иподключить ко всем трубам (вход, выход, ошибка)

4 голосов
/ 30 декабря 2015

Учитывая, что консенсус, кажется, предпочитает метод #sh граблей, но OP явно запрашивает bash, этот ответ может иметь некоторое применение.

Это актуально, поскольку Rake#sh использует вызов Kernel#system для запуска команд оболочки. Ruby жестко кодирует его как /bin/sh, игнорируя настроенную оболочку пользователя или $SHELL в среде.

Вот обходной путь, который вызывает 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_heredoc заимствовано из рельсов:

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

Вероятно, вы могли бы получить это, потребовав active_support, или, может быть, он автоматически загружается, когда вы находитесь в проекте rails, но я использовал этот внешний rails и поэтому должен был сам его определить.

Есть два heredocs: внешний с маркерами EOS и внутренний с маркерами BASH.

Способ, которым это работает, заключается в подаче внутреннего heredoc между маркерами BASH на стандартный ввод bash. Обратите внимание, что он работает в контексте /bin/sh, так что это posix heredoc, а не ruby. Обычно для этого требуется, чтобы маркер конца находился в столбце 1, что здесь не так из-за отступа.

Однако, поскольку он заключен в рубиновый heredoc, применяемый там метод strip_heredoc определяет его, помещая всю левую сторону внутреннего heredoc в столбец 1 до того, как /bin/sh увидит его.

/bin/sh также обычно расширяет переменные внутри heredoc, что может помешать скрипту. Одинарные кавычки вокруг начального маркера 'BASH' говорят /bin/sh не расширять что-либо внутри heredoc до того, как оно передается в bash.

Однако /bin/sh по-прежнему применяет экранирование к строке перед передачей в bash. Это означает, что обратные слэши должны быть удвоены, чтобы пройти через /bin/sh для bash, то есть \ становится \\.

Опции bash -xeuo pipefail являются необязательными.

Аргументы -euo pipefail заставляют bash работать в строгом режиме, который останавливает выполнение при любом сбое или обращении к неопределенной переменной, даже команде в конвейере. Это вернет ошибку rake, которая остановит задачу rake. Обычно это то, что вы хотите. Аргументы могут быть отброшены, если вы хотите нормальное поведение bash.

Опция -x для bash и аргумент {verbose: false} для #sh работают согласованно, так что rake печатает только команды bash, которые фактически выполняются. Это полезно, если ваш bash-скрипт не предназначен для выполнения полностью, например, если у него есть тест, который позволяет ему корректно завершить работу в начале скрипта.

Будьте внимательны и не устанавливайте код выхода, отличный от 0, если вы не хотите, чтобы задача rake провалилась. Обычно это означает, что вы не хотите использовать какие-либо конструкции || exit без явной установки кода выхода, т.е. || exit 0.

Если по пути вы столкнетесь с какой-нибудь странностью, отключите -o pipefail. Я видел некоторую ошибку, связанную с этим, особенно при конвейерной передаче к grep.

3 голосов
/ 21 марта 2012

%{echo "World!"} определяет строку. Я ожидаю, что вы хотели %x{echo "World!"}.

%x{echo "World!"} выполняет команду и возвращает вывод (stdout). Вы не увидите результат. Но вы можете сделать:

puts %x{echo "World!"}

Есть еще несколько способов вызова системной команды:

  • Backticks: `
  • system( cmd )
  • popen
  • Open3#popen3
0 голосов
/ 28 апреля 2019

Есть два способа:

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 ) будет только выполнять выражение оболочки, но стандартный вывод не будет отображаться на экране. Итак, вам лучше использовать %x( expr ), когда вам нужен результат команды.

Но если вы просто хотите выполнить команду оболочки, я рекомендую вам использовать sh "expr". Потому что sh "irb" заставит вас войти в оболочку irb, а %x(irb) умрет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...