Учитывая, что консенсус, кажется, предпочитает метод #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
.