2012-03-20 19 views
45

Vorrei eseguire un numero di comandi bash da un Rakefile. La mia shell attiva è bash e chiamerò rake da bash.Esegui comandi bash da un Rakefile

ho incluso nel mio Rakefile seguente

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

ma al momento l'esecuzione rake hello non c'è uscita?

Come si eseguono i comandi di bash da un RakeFile?

NOTA: Questo non è un duplicato come è specificamente chiedendo come eseguire i comandi bash da un Rakefile.

+0

Non è '% {', è '% x (', che restituisce stdout come stringa invece di stamparlo. –

+7

Non è un duplicato come sopra, in quanto richiede espressamente l'esecuzione in un file rake e – Gizmomogwai

+0

Come mai è contrassegnato come duplicato quando richiede in modo specifico Rake anziché Ruby in generale ?! – silverdr

risposta

101

I pensare il modo rake vuole che questo accada è con: http://rubydoc.info/gems/rake/FileUtils#sh-instance_method Esempio:

task :test do 
    sh "ls" 
end 

La funzione incorporata rake sh si occupa del valore di ritorno del comando (l'operazione fallisce se il comando ha un valore di ritorno diverso 0) e in aggiunta emette anche i comandi in uscita.

+0

Cosa intendi nel modo in cui il rastrello vuole che ciò accada? – rudolph9

+5

Le altre soluzioni come l'esecuzione con backtick e così via non sono metodi rake ma metodi ruby ​​(vedi http://zhangxh.net/programming/ruby/6-ways-to-run-shell-commands-in-ruby/ per 6 diversi modi). L'azione del rastrello nella funzione "sh" è di occuparsi automaticamente del valore di ritorno del comando, in modo che l'attività di rake non vada a buon fine, se un comando in sh non riesce. Il più delle volte questo è proprio quello che vuoi. Gli altri modi per eseguire comandi da ruby ​​non dovrebbero essere usati spesso dal rake. – Gizmomogwai

+3

un altro vantaggio del sistema sh over: sh sembra essere più prolisso sui comandi echo, di default. –

55

Esistono diversi modi per eseguire i comandi di shell in ruby. Un semplice (e probabilmente il più comune) è quello di utilizzare backticks:

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

backtick hanno un piacevole effetto in cui lo standard output del comando di shell diventa il valore di ritorno. Così, per esempio, è possibile ottenere l'output di ls facendo

shell_dir_listing = `ls` 

ma ci sono molti altri modi per chiamare i comandi della shell e tutti hanno benefici/svantaggi e funzionano in modo diverso. This article spiega le scelte di dettaglio, ma ecco un rapido possibilità di sintesi:

+0

Registra l'output in console, il che può essere un po 'confuso. 'sh" some_task "' per fare in modo che il comando stampi sul terminale come se lo si eseguisse direttamente dal terminale. – Automatico

3

%{echo "World!"} definisce una stringa. Mi aspetto che tu voglia %x{echo "World!"}.

%x{echo "World!"} esegue il comando e restituisce l'output (stdout). Non vedrai il risultato.Ma si può fare:

puts %x{echo "World!"} 

ci sono più modi per chiamare un comando di sistema:

  • backticks: `
  • system(cmd)
  • popen
  • Open3#popen3
+1

Si noti che '% x' e i backtick stanno effettivamente chiamando il metodo equivalente del kernel ruby, sono la sintassi alternativa per lo stesso metodo esatto –

+0

'' system (cmd) 'ha funzionato per me. Stavo cercando di scrivere uno script di generatore di pdf usando wkhtmltopdf. – tsega

2

Dato che il consenso sembra preferire il metodo di rake #sh, ma OP richiede esplicitamente bash, questa risposta potrebbe essere utile.

Questo è rilevante poiché Rake#sh utilizza la chiamata Kernel#system per eseguire i comandi della shell. Ruby hardcodes quello a /bin/sh, ignorando la shell configurata dell'utente o $SHELL nell'ambiente.

Ecco una soluzione che invoca bash da /bin/sh, che consente di utilizzare ancora il metodo 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 è preso in prestito da rotaie:

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

Probabilmente si potrebbe ottenere da richiede active_support, o forse è caricato automaticamente quando sei in un progetto di rotaie, ma stavo usando questo binario esterno e quindi ho dovuto rimuoverlo da solo.

Ci sono due heredocs, uno esterno con i marcatori EOS e uno interno con i marcatori BASH.

Il modo in cui funziona è alimentando l'heredoc interno tra i marcatori BASH allo stdin di bash. Si noti che è in esecuzione nel contesto di /bin/sh, quindi è un heredoc posix non rubino. Normalmente ciò richiede che il marcatore finale si trovi nella colonna 1, che non è il caso qui a causa del rientro.

Tuttavia, poiché è racchiuso all'interno di un heredoc rosso rubino, il metodo strip_heredoc applicato qui lo deindente, posizionando l'intera parte sinistra dell'e-edoc interno nella colonna 1 precedente a /bin/sh vedendolo.

/bin/sh inoltre espanderebbe normalmente le variabili all'interno di heredoc, che potrebbero interferire con lo script. Le virgolette singole attorno al marcatore di inizio, 'BASH', dicono a /bin/sh di non espandere nulla dentro l'heredoc prima che sia passato a bash.

Tuttavia /bin/sh applica ancora gli escape alla stringa prima di passarla a bash. Ciò significa che le escape con backslash devono essere raddoppiate per passare da /bin/sh a bash, ad es.\ diventa \\.

Le opzioni di bash -xeuo pipefail sono facoltative.

Gli argomenti -euo pipefail dicono a bash di essere eseguito in modalità rigorosa, che interrompe l'esecuzione in caso di errore o riferimento a una variabile non definita, persino un comando in una pipeline. Ciò restituirà un errore al rake, che interromperà il rake. Di solito questo è quello che vuoi. Gli argomenti possono essere abbandonati se si desidera un comportamento bash normale.

L'opzione -x per bash e l'argomento {verbose: false} su #sh funzionano in modo che il rake stampi solo i comandi di bash effettivamente eseguiti. Ciò è utile se lo script di bash non è pensato per essere eseguito nella sua interezza, ad esempio se ha un test che gli consenta di uscire graziosamente all'inizio dello script.

Fare attenzione a non impostare un codice di uscita diverso da 0 se non si desidera che l'attività rake non abbia esito positivo. Di solito ciò significa che non si desidera utilizzare alcun costrutto || exit senza impostare esplicitamente il codice di uscita, ad esempio || exit 0.

Se si incontra qualsiasi stranezza di bash lungo il percorso, disattivare -o pipefail. Ho visto un po 'di bug relativi ad esso specificamente durante il pipelining a grep.

+1

Grazie. Questo mi ha aiutato guidando nella giusta direzione. Lo sto usando in uno script rubino in cui ho bisogno di chiamare alcuni metodi che hanno bisogno di bash. Uso anche una lunga stringa con alcuni personaggi "strani". Ecco come lo faccio, forse può aiutare qualcuno. 'system (% [xx = $ (cat << EOT \ n # {corpo_text} \ nEOT \ n); bash << BASH \ n. # {ENV ['HOME']} /. bash_profile; echo" $ xx " | own_mail -TRm \ nBASH \ n)] '. Il modo più semplice sarebbe quello di inserire tutto in uno script bash e chiamare semplicemente 'system (" path/my_bash_script.sh ")' – 244an

Problemi correlati