2009-07-28 16 views
7

Se sto scrivendo uno script di shell e voglio "generare" alcuni script di shell esterni (c-) per configurare il mio ambiente, posso semplicemente effettuare chiamate come questa:Script di shell di origine in ambiente all'interno di uno script rubino

source /file/I/want/to/source.csh

Voglio sostituire uno script di shell che esegue questo con uno script ruby. Posso fare una cosa simile nello script ruby?

Aggiornamento:

appena provato con test_script.csh:

#!/bin/csh 

setenv HAPPYTIMES True

... e test_script.rb:

#!/usr/bin/env ruby 
system "~/test_script.csh" 
system "echo $HAPPYTIMES"

Purtroppo, non HAPPYTIMES come ancora.

+0

Cosa ottieni se cambi il tuo script con questo? #!/Usr/bin/env ruby ​​ mette ENV ['HAPPYTIMES'] – Henry

+0

è una cosa degna di accedere alle funzioni di tipo di sistema operativo come l'impostazione dell'ambiente dalle lingue. Python ha os.environ, c'è un modulo simile in Ruby come in python? cf http://stackoverflow.com/questions/4906977/python-environment-variables e http://docs.python.org/2/library/os.html – bootload

+0

possibile duplicato di [Uscire da ruby ​​mentre si imposta una variabile di ambiente ] (http://stackoverflow.com/questions/8301294/shell-out-from-ruby-while-setting-an-environment-variable) – durron597

risposta

4

Il motivo per cui questo non funziona per voi è b/c rubino corre i suoi comandi system in shell separate. Quindi, quando un comando di sistema termina, la shell che ha originato il tuo file si chiude e tutte le variabili di ambiente impostate in quella shell vengono dimenticate.

Se non si conosce il nome del file di origine fino al runtime, quindi Roboprog's answer è un buon approccio. Tuttavia, se si conosce il nome del file originario in anticipo, è possibile eseguire un attacco rapido con la riga hashbang.

% echo sourcer.rb 
#!/usr/bin/env ruby 
exec "csh -c 'source #{ARGV[0]} && /usr/bin/env ruby #{ARGV[1]}'" 
% echo my-script.rb 
#!/usr/bin/env ruby sourcer.rb /path/to/file/I/want/to/source.csh 
puts "HAPPYTIMES = #{ENV['HAPPYTIMES']}" 
% ./my-script.rb 
HAPPYTIMES = True 

Tutte queste solo aiuterà a utilizzare le variabili insieme enviroment nello script rubino, non metterli nella shell (dato che sono dimenticati non appena completato il processo Ruby). Per questo, sei bloccato con il comando source.

+0

Suppongo che l'unica ragione per cui questo funzioni, per esempio, csh, è che stai usando la stessa istanza di shell per interpretare lo script che stai procurando. Quindi non si poteva importare un file .csh da uno script bash più di quanto si potesse ricavare da uno script ruby ​​... – Charlie

-4
system 'source /file/I/want/to/source.sh' 

Non sono sicuro che ciò farà ciò che desideri. Eseguirà il comando source in una subshell. Provalo e vedilo fa quello che cerchi.

+0

Ho provato questo e non sembra fare ciò che sto cercando. Sto cercando di ottenere le variabili di ambiente impostate dallo script di shell nell'ambiente in cui è in esecuzione lo script di ruby. Le modifiche di ambiente – Charlie

+0

in un processo figlio non influiscono sul genitore –

+0

Ho pensato che potrebbe essere il caso. Scusa per aver perso tempo. – Henry

1

si sta andando ad avere per scrivere una funzione per eseguire qualcosa di simile a quanto segue, e catturare l'uscita (operazione "backtick"):

/bin/csh -e '. my_script ; env' 

loop su ogni linea, partita contro qualcosa come

/^(\w+)=(.*)$/ 

Quindi utilizzare la prima acquisizione della corrispondenza come nome var e la seconda acquisizione come valore var.

(sì, sto copertura sul fatto che io so Perl modo migliore di Ruby, ma l'approccio sarebbe lo stesso)

+0

In realtà, ciò che ho fatto catturare TUTTE le variabili d'ambiente dopo aver eseguito lo script. IFF le assegnazioni nello "script" non hanno valori interpolati, solo stringhe letterali, è possibile ottenere ciò che si vuole semplicemente leggendo il file, piuttosto che doverlo eseguire su una pipe (backtick). – Roboprog

4

dato i seguenti Rubino

# Read in the bash environment, after an optional command. 
# Returns Array of key/value pairs. 
def bash_env(cmd=nil) 
    env = `#{cmd + ';' if cmd} printenv` 
    env.split(/\n/).map {|l| l.split(/=/)} 
end 

# Source a given file, and compare environment before and after. 
# Returns Hash of any keys that have changed. 
def bash_source(file) 
    Hash[ bash_env(". #{File.realpath file}") - bash_env() ] 
end 

# Find variables changed as a result of sourcing the given file, 
# and update in ENV. 
def source_env_from(file) 
    bash_source(file).each {|k,v| ENV[k] = v } 
end 

e la seguente test.sh:

#!/usr/bin/env bash 
export FOO='bar' 

si dovrebbe ottenere:

irb(main):019:0> source_env_from('test.sh') 
=> {"FOO"=>"bar"} 
irb(main):020:0> ENV['FOO'] 
=> "bar" 

Enjoy!

2

Ho avuto lo stesso probrem. e mi risolvo come sotto.

#!/usr/local/bin/ruby 

def source(filename) 
    ENV.replace(eval(`tcsh -c 'source #{filename} && ruby -e "p ENV"'`)) 
end 

p "***old env*****************************" 
p ENV 
source "/file/I/want/to/source.csh" 
p "+++new env+++++++++++++++++++++++++++++" 
p ENV 

'eval' è un metodo molto potente. Saltare facilmente il processo.

+0

È necessario fare molta attenzione se 'filename' è in realtà ciò che si aspetta che sia; se si tratta di un input da parte dell'utente, si tratta di un'enorme perdita di sicurezza ... Di solito non si vogliono generare file arbitrari, quindi un modo più sicuro per farlo potrebbe essere il presupposto che 'filename' sia un simbolo, e lo si osservi in ​​alto un 'Hash' (e se non esiste, genera un errore). – Carpetsmoker

2

Migliorare un po 'la risposta di @ takeccho ... Controlli e alcuni fischi. Innanzitutto, l'ambiente di origine viene pulito tramite env -i, che è una misura di sicurezza ma potrebbe non essere desiderato in alcuni casi. In secondo luogo, tramite set -a, tutte le variabili impostate nel file vengono "esportate" dalla shell e quindi importate in ruby. Questo è utile per simulare/sovrascrivere il comportamento trovato nei file di ambiente usati con gli script init e i file systemd env.

def ShSource(filename) 
    # Inspired by user takeccho at http://stackoverflow.com/a/26381374/3849157 
    # Sources sh-script or env file and imports resulting environment 
    fail(ArgumentError,"File #{filename} invalid or doesn't exist.") \ 
    unless File.exist?(filename) 

    _newhashstr=`env -i sh -c 'set -a;source #{filename} && ruby -e "p ENV"'` 
    fail(ArgumentError,"Failure to parse or process #{filename} environment")\ 
    unless _newhashstr.match(/^\{("[^"]+"=>".*?",\s*)*("[^"]+"=>".*?")\}$/) 

    _newhash=eval(_newhashstr) 
    %w[ SHLVL PWD _ ].each{|k|_newhash.delete(k) } 
    _newhash.each{|k,v| ENV[k]=v } # ENV does not have #merge! 
end 

Principio di funzionamento: Quando rubino emette dell'oggetto ENV usando p, lo fa in un modo che rubino può leggere nuovamente come un oggetto. Quindi usiamo la shell per generare il file di destinazione e ruby ​​(in una sub-shell) per produrre l'ambiente in quella forma serializzabile. Quindi acquisiamo l'output di ruby ​​e lo eval all'interno del nostro processo di ruby. Chiaramente questo non è privo di rischi, quindi per mitigare il rischio, noi (1) convalidiamo il nome del file che è passato e (2) convalidiamo usando una regexp che la cosa che torniamo dalla sottotitola ruby ​​è, infatti, una stringa hash serializzabile. Una volta sicuri, eseguiamo lo eval che crea un nuovo hash. Quindi "manualmente" unire l'hash con ENV, che è un Object e non un normale Hash. Se si trattasse di un hash, avremmo potuto utilizzare il metodo #merge!.

MODIFICA: sh -a elementi esportati come PATH. Dobbiamo anche rimuovere SHLVL e PWD prima della hash-unione.

Problemi correlati