Führen Sie Bash-Befehle aus einem Rakefile aus

75

Ich möchte eine Reihe von bashBefehlen von a ausführen Rakefile.

Ich habe folgendes in meinem versucht Rakefile

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

aber bei der Ausführung rake hellogibt es keine Ausgabe? Wie führe ich Bash-Befehle aus einem Rakefile aus?

HINWEIS : Dies ist kein Duplikat, da speziell gefragt wird, wie Bash-Befehle aus einem Rakefile ausgeführt werden sollen .

rudolph9
quelle
Es ist nicht %{so %x(, und das gibt stdout als Zeichenfolge zurück, anstatt es zu drucken.
Ciro Santilli 法轮功 冠状 病 六四 事件 18
8
Es handelt sich nicht um ein Duplikat wie oben angegeben, da speziell die Ausführung in einer Rake-Datei und nicht in einem regulären Ruby-Programm angefordert wird.
Gizmomogwai
Wie kommt es, dass es als Duplikat markiert ist, wenn es speziell nach Rake und nicht nach Ruby im Allgemeinen fragt?!
Silverdr

Antworten:

131

Ich denke, Rake möchte, dass dies geschieht mit: http://rubydoc.info/gems/rake/FileUtils#sh-instance_method Beispiel:

task :test do
  sh "ls"
end

Die eingebaute Rake-Funktion sh kümmert sich um den Rückgabewert des Befehls (die Task schlägt fehl, wenn der Befehl einen anderen Rückgabewert als 0 hat) und gibt zusätzlich die ausgegebenen Befehle aus.

Gizmomogwai
quelle
7
Die anderen Lösungen wie das Ausführen mit Backticks usw. sind keine Rake-Methoden, sondern Ruby-Methoden ( 6 verschiedene Möglichkeiten finden Sie unter zhangxh.net/programming/ruby/… ). Was Rake in der "sh" -Funktion tut, ist, sich automatisch um den Rückgabewert des Befehls zu kümmern, so dass die Rake-Task fehlschlägt, wenn ein Befehl in sh fehlschlägt. Meistens ist dies genau das, was Sie wollen. Die anderen Möglichkeiten, Befehle von Ruby auszuführen, sollten nicht so oft von Rake verwendet werden.
Gizmomogwai
4
Ein weiterer Vorteil von sh gegenüber dem System: sh scheint standardmäßig ausführlicher über das Echo von Befehlen zu sein.
Luke W
4
Ich hatte den besten Erfolg mit der Verwendung von sh in Rake-Dateien anstelle von System.
Orkoden
3
Der Link ist seit Juli 2014 tot, da RubyForge nicht mehr verfügbar ist.
Arto Bendiken
1
@ArtoBendiken hat den Kommentar aktualisiert und enthält nun einen aktuellen Link zu rdoc.
Gizmomogwai
59

Es gibt verschiedene Möglichkeiten, Shell-Befehle in Ruby auszuführen. Ein einfaches (und wahrscheinlich das häufigste) ist die Verwendung von Backticks:

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

Backticks haben einen schönen Effekt, bei dem die Standardausgabe des Shell-Befehls zum Rückgabewert wird. So können Sie beispielsweise die Ausgabe erhalten, lsindem Sie dies tun

shell_dir_listing = `ls`

Es gibt jedoch viele andere Möglichkeiten, Shell-Befehle aufzurufen, und alle haben Vor- und Nachteile und funktionieren unterschiedlich. In diesem Artikel werden die Auswahlmöglichkeiten ausführlich erläutert. Hier finden Sie eine kurze Zusammenfassung der Möglichkeiten:

  • stdout =% x {cmd} - Alternative Syntax für Backticks, hinter den Kulissen macht es dasselbe

  • exec (cmd) - Ersetzen Sie den laufenden Prozess vollständig durch einen neuen cmd-Prozess

  • success = system (cmd) - Führen Sie einen Unterprozess aus und geben Sie bei Erfolg / Misserfolg true / false zurück (basierend auf dem Status des cmd-Exits).

  • IO # popen (cmd) {| io | } - Führen Sie einen Unterprozess aus und verbinden Sie stdout und stderr mit io

  • stdin, stdout, stderr = Open3.popen3 (cmd) - Führen Sie einen Unterprozess aus und stellen Sie eine Verbindung zu allen Pipes her (in, out, err).

Ben Lee
quelle
Dadurch wird die Ausgabe in der Konsole protokolliert, was etwas verwirrend sein kann. In Rake können Sie sh "some_task"den Befehl so drucken lassen, als ob Sie ihn direkt vom Terminal ausgeführt hätten.
Automatico
6

Angesichts der Tatsache, dass der Konsens die #shMethode von Rake zu bevorzugen scheint , OP jedoch ausdrücklich Bash anfordert, kann diese Antwort von Nutzen sein.

Dies ist relevant, da Rake#shder Kernel#systemAufruf zum Ausführen von Shell-Befehlen verwendet wird. Ruby codiert das fest /bin/shund ignoriert dabei die konfigurierte Shell des Benutzers oder $SHELLdie Umgebung.

Hier ist eine Problemumgehung, mit der bash aufgerufen wird /bin/sh, sodass Sie die shMethode weiterhin verwenden können :

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 wird von Schienen ausgeliehen:

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

Sie könnten es wahrscheinlich erhalten, indem Sie active_support benötigen, oder es wird automatisch geladen, wenn Sie sich in einem Rails-Projekt befinden, aber ich habe diese externen Rails verwendet und musste es daher selbst definieren.

Es gibt zwei Heredocs, einen äußeren mit den Markern EOSund einen inneren mit den Markern BASH.

Die Art und Weise, wie dies funktioniert, besteht darin, den inneren Heredoc zwischen den BASH-Markern dem Bash-Standard zuzuführen. Beachten Sie, dass es im Kontext von ausgeführt wird/bin/sh , es ist also ein Posix Heredoc, kein Ruby. Normalerweise muss sich die Endmarkierung in Spalte 1 befinden, was hier aufgrund des Einrückens nicht der Fall ist.

Da es jedoch in einen Rubin-Heredoc eingewickelt ist, wird es durch die dort strip_heredocangewandte Methode entfernt und die gesamte linke Seite des inneren Heredocs in Spalte 1 platziert, bevor es /bin/shangezeigt wird.

/bin/shwürde normalerweise auch Variablen innerhalb des Heredocs erweitern, was das Skript stören könnte. Die einfachen Anführungszeichen um den Startmarker 'BASH' weisen darauf hin, dass /bin/shim Heredoc nichts erweitert werden soll, bevor es an bash übergeben wird.

Wendet /bin/shjedoch immer noch Escapezeichen auf die Zeichenfolge an, bevor sie an bash übergeben wird. Das bedeutet, dass Backslash-Fluchten verdoppelt werden müssen, um /bin/shzu Bash zu gelangen, dh \zu werden\\ .

Die Bash-Optionen -xeusind optional.

Die -euArgumente weisen bash an, im strengen Modus ausgeführt zu werden, wodurch die Ausführung bei einem Fehler oder Verweis auf eine undefinierte Variable gestoppt wird. Dies gibt einen Fehler an Rake zurück, wodurch die Rake-Task gestoppt wird. Normalerweise ist dies das, was Sie wollen. Die Argumente können gelöscht werden, wenn Sie ein normales Bash-Verhalten wünschen.

Die -xOption zum Bash und {verbose: false}Argumentieren, um gemeinsam zu #sharbeiten, sodass Rake nur die Bash-Befehle druckt, die tatsächlich ausgeführt werden. Dies ist nützlich, wenn Ihr Bash-Skript nicht vollständig ausgeführt werden soll, z. B. wenn es einen Test enthält, mit dem es ordnungsgemäß im Skript beendet werden kann.

Achten Sie darauf, keinen anderen Exit-Code als 0 festzulegen, wenn die Rake-Task nicht fehlschlagen soll. Normalerweise bedeutet dies, dass Sie keine || exitKonstrukte verwenden möchten, ohne den Exit-Code explizit festzulegen, d || exit 0. H.

Binäres Phile
quelle
1
Vielen Dank. Dies hat mir geholfen, indem ich in die richtige Richtung geführt habe. Ich verwende es in einem Ruby-Skript, in dem ich einige Methoden aufrufen muss, die Bash benötigen. Ich benutze auch eine lange Zeichenfolge mit einigen "seltsamen" Zeichen. Hier ist, wie ich es mache, vielleicht kann es jemandem helfen. system(%[xx=$(cat <<EOT\n#{body_text}\nEOT\n); bash <<BASH\n. #{ENV['HOME']}/.bash_profile; echo "$xx" | own_mail -TRm\nBASH\n)]. Der einfachste Weg wäre jedoch, alles in ein Bash-Skript zu schreiben und es einfach so zu nennensystem("path/my_bash_script.sh")
244
5

%{echo "World!"}definiert einen String. Ich nehme an, du wolltest %x{echo "World!"}.

%x{echo "World!"}führt den Befehl aus und gibt die Ausgabe zurück (stdout). Sie werden das Ergebnis nicht sehen. Aber Sie können tun:

puts %x{echo "World!"}

Es gibt weitere Möglichkeiten, einen Systembefehl aufzurufen:

  • Backticks: `
  • system( cmd )
  • popen
  • Open3#popen3
knut
quelle
1
Es sollte beachtet werden, dass %xund Backticks tatsächlich die äquivalente Ruby-Kernel-Methode aufrufen, sie sind alternative Syntax für dieselbe exakte Methode.
Ben Lee
1
system( cmd )arbeitete für mich. Ich habe versucht, ein PDF-Generator-Skript mit wkhtmltopdf zu schreiben.
Tsega
1

Es gibt zwei Möglichkeiten:

sh " expr "

oder

%x( expr )

Beachten Sie, dass (Ausdruck) {Ausdruck}, | sein kann Ausdruck | oder `expr`

Der Unterschied ist, dass sh "expr" eine Ruby-Methode ist, um etwas auszuführen, und% x (expr) ist die in Ruby integrierte Methode. Das Ergebnis und die Aktion sind unterschiedlich. Hier ist ein Beispiel

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

erhalten:

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

Sie können sehen, dass %x( expr )nur der Shell-Ausdruck ausgeführt wird, der Standard jedoch nicht auf dem Bildschirm angezeigt wird. Also, du solltest es besser benutzen%x( expr ) wenn Sie das Befehlsergebnis benötigen.

Wenn Sie jedoch nur einen Shell-Befehl ausführen möchten, empfehle ich die Verwendung sh "expr". Denn sh "irb"du wirst in die Irb-Muschel gehen, während %x(irb)du tot bist .

Neo li
quelle