Виконайте команди bash з Rakefile


76

Я хотів би виконати декілька bashкоманд з Rakefile.

Я спробував наступне у своєму Rakefile

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

але при виконанні rake helloнемає результату? Як виконувати команди bash з Rakefile?

ПРИМІТКА : Це не дублікат, оскільки він конкретно запитує, як виконувати команди bash з Rakefile .


Це ні %{, це %x(, і це повертає stdout як рядок, замість того, щоб його друкувати.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

8
Це не дублікат, як зазначено вище, оскільки він конкретно вимагає виконання у файлі граблі, а не в звичайній рубіновій програмі.
Gizmomogwai

Як так, це позначено як дублікат, коли він запитує конкретно про Рейка, а не про Рубі взагалі ?!
silverdr

Відповіді:


131

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

task :test do
  sh "ls"
end

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


7
Інші рішення, такі як виконання із зворотними позначками тощо, не є методами згрібання, а методами рубіну (див. Zhangxh.net/programming/ruby/… для 6 різних способів). Функція rake у функції "sh" полягає в тому, щоб автоматично подбати про повернене значення команди, так що завдання rake не вдається, якщо одна команда в sh не вдається. Здебільшого це саме те, що ви хочете. Інші способи виконання команд з рубіну не слід використовувати так часто з граблі.
Gizmomogwai

4
ще одна перевага sh над системою: sh, здається, за замовчуванням більш детальний щодо команд відлуння.
Luke W

4
Я мав найкращий успіх із використанням sh у файлах rake замість системи.
orkoden

3
Посилання мертве станом на липень 2014 року, враховуючи, що RubyForge вже немає.
Арто Бендікен

1
@ArtoBendiken оновив коментар, включивши сучасне посилання на rdoc.
Gizmomogwai

59

Існує кілька способів виконувати команди оболонки в 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) - Запустіть підпроцес і підключіться до всіх труб ( вхід , вихід, помилка)


Це реєструє вихід на консолі, що може трохи заплутати. У рейці ви можете використовувати sh "some_task"команду, що друкується на терміналі, як якщо б ви запускали її прямо з терміналу.
Автоматично,

7

Враховуючи, що консенсус, схоже, віддає перевагу #shметоду rake , але 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 -xeu <<'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, або, можливо, він завантажується автоматично, коли ви перебуваєте у проекті рейок, але я використовував це зовнішні рейки, і тому мені довелося його самому захищати.

Є два гередоки, зовнішній із маркерами EOSта внутрішній із маркерами BASH.

Це працює шляхом подачі внутрішнього гередоку між маркерами BASH до stdin bash. Зверніть увагу, що він працює в контексті /bin/sh, тому це posix heredoc, а не рубіновий. Зазвичай для цього потрібно, щоб кінцевий маркер знаходився у стовпці 1, що тут не так через відступ.

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

/bin/shтакож, як правило, розширює змінні всередині heredoc, що може перешкоджати сценарію. /bin/shПоодинокі лапки навколо маркера старту, 'BASH', вказують не розширювати нічого всередині heredoc, перш ніж він буде переданий bash.

Однак /bin/shвсе ще застосовує екранування до рядка перед передачею його в bash. Це означає, що екрани зворотної риски слід подвоїти, щоб пройти через /bin/shbash, тобто \стає \\.

Параметри bash -xeuнеобов’язкові.

В -euаргументах кажуть Башу працювати в суворому режимі, який зупиняє виконання за будь-якої несправності або посилань на невизначений змінний. Це поверне помилку рейку, що зупинить завдання граблі. Зазвичай, це те, що ви хочете. Аргументи можна скинути, якщо ви хочете нормальної поведінки bash.

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

Будьте обережні, не встановлюйте код виходу, відмінний від 0, якщо ви не хочете, щоб завдання граблі провалилося. Зазвичай це означає, що ви не хочете використовувати будь-які || exitконструкції без явного встановлення коду виходу, тобто || exit 0.


1
Дякую. Це допомогло мені, ведучи в правильному напрямку. Я використовую його в рубіновому сценарії, де мені потрібно викликати деякі методи, які потребують bash. Я також використовую довгий рядок з деякими "дивними" символами. Ось як я це роблю, можливо, це може комусь допомогти. 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")
244,

5

%{echo "World!"}визначає рядок. Я сподіваюся, ти хотів %x{echo "World!"}.

%x{echo "World!"}виконує команду і повертає результат (stdout). Ви не побачите результату. Але ви можете зробити:

puts %x{echo "World!"}

Існує більше способів викликати системну команду:

  • Зворотні мітки: `
  • system( cmd )
  • popen
  • Open3#popen3

1
Слід зазначити, що %xі зворотні посилання насправді викликають еквівалентний метод рубінового ядра, вони є альтернативним синтаксисом того самого методу.
Ben Lee

1
system( cmd )працював у мене. Я намагався написати сценарій генератора PDF за допомогою wkhtmltopdf.
tsega

1

Є два шляхи:

sh " expr "

або

%x( expr )

Пам’ятайте, що (expr) може бути {expr}, | вираз | або `expr`

Різниця полягає в тому, що sh "expr" - це рубіновий метод для виконання чогось, а% 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 )буде робити лише оболонку expr, але stdout не відображатиметься на екрані. Отже, вам краще використовувати, %x( expr )коли вам потрібен результат команди.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.