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
.
Non è '% {', è '% x (', che restituisce stdout come stringa invece di stamparlo. –
Non è un duplicato come sopra, in quanto richiede espressamente l'esecuzione in un file rake e – Gizmomogwai
Come mai è contrassegnato come duplicato quando richiede in modo specifico Rake anziché Ruby in generale ?! – silverdr