2009-07-28 16 views
7

Si estoy escribiendo un script de shell y quiero "fuente" algunos scripts de shell externos (c-) para configurar mi entorno, puedo hacer llamadas como esta:Script fuente de shell en el entorno dentro de un script de ruby ​​

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

Quiero reemplazar un script de shell que hace esto con un script de ruby. ¿Puedo hacer algo similar en el script de ruby?

Actualización:

sólo trató con test_script.csh:

#!/bin/csh 

setenv HAPPYTIMES True

... y test_script.rb:

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

Lamentablemente, no hay happytimes hasta el momento.

+0

¿Qué obtienes si cambias tu script a esto? #!/Usr/bin/env ruby ​​ pone ENV ['HAPPYTIMES'] – Henry

+0

es una buena cosa para tener acceso a las funciones de tipo sistema operativo como la configuración del entorno de los idiomas. Python tiene os.environ, ¿hay un módulo similar en Ruby que en python? cf http://stackoverflow.com/questions/4906977/python-environment-variables & http://docs.python.org/2/library/os.html – bootload

+0

posible duplicado de [Shell out from ruby ​​al establecer una variable de entorno ] (http://stackoverflow.com/questions/8301294/shell-out-from-ruby-while-setting-an-environment-variable) – durron597

Respuesta

4

La razón de esto no está funcionando para usted es b/c rubí funciona sus comandos system en conchas separadas. Por lo tanto, cuando termina un comando del sistema, se cierra el shell que tenía el origen del archivo y se olvidan todas las variables de entorno establecidas en ese shell.

Si no conoce el nombre del archivo de origen hasta el tiempo de ejecución, entonces Roboprog's answer es un buen enfoque. Sin embargo, si conoce el nombre del archivo de origen con anticipación, puede hacer un hack rápido con la línea de 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 

Todos estos sólo le ayudará a utilizar las variables conjunto de medio ambiente en el script Ruby, no lo haga en su concha (ya que están olvidados tan pronto como el proceso de rubí completa). Para eso, estás atrapado con el comando source.

+0

Supongo que la única razón por la que esto funciona en, por ejemplo, csh, es que está utilizando la misma instancia de shell para interpretar el script que está obteniendo. Por lo tanto, no puede buscar un archivo .csh a partir de un script bash más de lo que podría obtenerlo de un script de ruby ​​... – Charlie

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

No estoy seguro de que esto haga lo que quiera. Ejecutará el comando fuente en una subcadena. Pruébalo y verás que hace lo que estás buscando.

+0

Intenté esto y no parece hacer lo que estoy buscando. Estoy intentando que las variables de entorno establecidas por el script de shell entren en el entorno en el que se ejecuta el script de ruby. – Charlie

+0

los cambios de entorno en un proceso hijo no afectan al padre –

+0

Creo que ese podría ser el caso. Lo siento por hacerte perder el tiempo. – Henry

1

usted va a tener que escribir una función para ejecutar algo como lo siguiente, y capturar la salida ("tilde" operación):

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

bucle en cada línea, partido contra algo así como

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

A continuación, utilice la primera coincidencia de captura como el nombre var, y la segunda captura como el valor var.

(sí, estoy de cobertura en el hecho de que sé Perl mucho mejor que Ruby, pero el enfoque sería el mismo)

+0

En realidad, lo que sí capturé TODAS las variables de entorno después de ejecutar el script. IFF las asignaciones en el "script" no tienen valores interpolados, solo literales de cadena, puede obtener lo que desea simplemente leer el archivo, en lugar de tener que ejecutarlo en una tubería (retroceso). – Roboprog

4

Dada la siguiente Rubí

# 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 

y la siguiente test.sh:

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

usted debe conseguir:

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

Enjoy!

2

Tuve la misma probremia. y lo resuelvo como a continuación.

#!/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' es un método muy potente. Salta sobre el proceso fácilmente.

+0

Debe tener mucho cuidado si 'filename' realmente es lo que espera que sea; si esta es la entrada del usuario, entonces es una gran pérdida de seguridad ... Por lo general, no desea buscar archivos arbitrarios, por lo que una forma más segura de hacerlo podría ser suponer que 'filename' es un símbolo, y busque eso en un 'Hash' (y si no existe, arroje un error). – Carpetsmoker

2

Mejorando un poco la respuesta de @ takeccho ... Comprobaciones y algunos silbidos. En primer lugar, el entorno de origen se limpia a través del env -i, que es una medida de seguridad, pero que podría no desearse en algunos casos. En segundo lugar, a través de set -a, todas las variables establecidas en el archivo se "exportan" desde el shell y, por lo tanto, se importan a ruby. Esto es útil para simular/anular el comportamiento que se encuentra en los archivos de entorno utilizados con los scripts init y los archivos 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 

Teoría de funcionamiento: Cuando rubí emite el objeto ENV usando p, lo hace de una manera que el rubí puede leer de nuevo en como un objeto. Entonces usamos el shell para obtener el archivo objetivo, y ruby ​​(en un subconjunto) para dar salida al entorno en esa forma serializable. Luego capturamos la salida de Ruby y eval dentro de nuestro proceso Ruby. Claramente esto no está exento de riesgos, por lo tanto, para mitigar el riesgo, (1) validamos el nombre de archivo que se transfiere y (2) validamos usando una expresión regular que lo que obtenemos de ruby-subshell es, de hecho, una cadena de trozos serializable. Una vez que estamos seguros de eso, hacemos el eval que crea un nuevo hash. Luego fusionamos "manualmente" el hash con ENV, que es un Object y no un Hash normal. Si fuera un Hash, podríamos haber usado el método #merge!.

EDITAR: sh -a cosas exportadas como PATH. También debemos eliminar SHLVL y PWD antes del hash-merge.

Cuestiones relacionadas