2009-07-28 12 views
7

Jeśli piszę skrypt powłoki i chcę "źródło" niektórych zewnętrznych (c-) skryptów powłoki, aby skonfigurować moje środowisko, mogę po prostu wykonywać takie połączenia:Skrypt powłoki źródłowej do środowiska wewnątrz skryptu ruby ​​

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

Chcę zastąpić skrypt powłoki, który robi to ze skryptem ruby. Czy mogę zrobić coś podobnego w skrypcie ruby?

Aktualizacja:

prostu próbowałem go test_script.csh:

#!/bin/csh 

setenv HAPPYTIMES True

... i test_script.rb:

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

Niestety, nie ma HAPPYTIMES jak dotąd.

+0

Co otrzymasz, jeśli zmienisz scenariusz na ten? #!/Usr/bin/env ruby ​​ stawia ENV ['HAPPYTIMES'] – Henry

+0

to godna rzecz, aby uzyskać dostęp do funkcji typu systemu operacyjnego, takich jak ustawienia środowiska z języków. Python ma os.environ, czy istnieje podobny moduł w Ruby jak w python? cf http://stackoverflow.com/questions/4906977/python-environment-variables & http://docs.python.org/2/library/os.html – bootload

+0

możliwy duplikat [Shell out from ruby ​​podczas ustawiania zmiennej środowiskowej ] (http://stackoverflow.com/questions/8301294/shell-out-frub-ruby-while-setting-an-environment-variable) – durron597

Odpowiedz

4

Powód to nie działa dla ciebie jest b/c rubin działa jego komendy system w osobnych powłokach. Tak więc, gdy zakończy się jedno polecenie systemowe, powłoka, z której pochodził twój plik, zostanie zamknięta, a wszelkie zmienne środowiskowe ustawione w tej powłoce zostaną zapomniane.

Jeśli nie znasz nazwy pliku źródłowego do czasu wykonania, to dobrym pomysłem jest Roboprog's answer. Jednakże, jeśli znasz nazwę pliku źródłowego z wyprzedzeniem, możesz zrobić szybkie hackowanie za pomocą linii 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 

Wszystko to pomoże tylko użyć zestawu zmiennych w skrypcie Enviroment ruby, nie ustawić je w skorupkach (ponieważ oni zapomnieli, jak tylko zakończy się proces rubinowy). W tym celu utknąłeś w poleceniu source.

+0

Zgaduję, że jedynym powodem, dla którego to działa, powiedzmy, csh, jest to, że używasz tej samej instancji powłoki do interpretowania skryptu, który pozyskujesz. Tak więc nie można było pobrać pliku .csh ze skryptu Bash, tak jak nie można go pobrać ze skryptu ruby ​​... – Charlie

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

Nie jestem pewien, czy zrobi to, co chcesz. Wykona polecenie source w podpowłoce. Spróbuj i przekonaj się, że robi to, o co prosisz.

+0

Próbowałem tego i wydaje się, że nie robię tego, co robię. Próbuję pobrać zmienne środowiskowe ustawione przez skrypt powłoki do środowiska, w którym działa skrypt ruby. – Charlie

+0

Zmiany środowiska w procesie potomnym nie mają wpływu na rodzica –

+0

Myślałem, że tak być może. Przepraszam że marnuje twój czas. – Henry

1

Będziesz musiał napisać funkcję, aby uruchomić coś w rodzaju czynności, a uchwycić wyjście („lewy apostrof” Operation):

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

pętli na każdej linii, mecz z czymś jak

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

Następnie użyj pierwszego uchwycenia dopasowania jako nazwy zmiennej, a drugie przechwyć jako wartość var.

(tak, jestem zabezpieczających na fakt, że wiem lepiej niż Perl Ruby, ale podejście byłoby takie samo)

+0

Właściwie to, co zrobiłem, przechwyciłem WSZYSTKIE zmienne środowiskowe po uruchomieniu skryptu. IFF przypisania w "skrypcie" nie mają wartości interpolowanych, tylko literały łańcuchowe, można uzyskać to, co chcesz, tylko czytając plik, zamiast uruchamiać go na rurze (backtick). – Roboprog

4

Biorąc pod uwagę następujące Ruby

# 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 

i następujący test.sh:

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

powinieneś dostać:

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

Enjoy!

2

Miałem ten sam probrem. i postanawiam jak poniżej.

#!/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" to bardzo potężna metoda. Łatwo przeskoczyć przez proces.

+0

Musisz być bardzo ostrożny, jeśli 'filename' faktycznie jest tym, czego oczekujesz; jeśli jest to dane wprowadzane przez użytkownika, to jest to ogromny wyciek bezpieczeństwa ... Zazwyczaj nie chcesz generować dowolnych plików, więc bezpieczniejszym sposobem na to może być założenie, że 'filename' jest symbolem, i spójrz na to w a 'Hash' (a jeśli nie istnieje, wyślij błąd). – Carpetsmoker

2

Ulepszanie odpowiedzi @ takeccho ... Czeki i kilka gwizdów. Po pierwsze, środowisko źródłowe jest czyszczone przez env -i, co jest środkiem bezpieczeństwa, ale w niektórych przypadkach może być niepożądane. Po drugie, poprzez set -a, wszystkie zmienne ustawione w pliku są "eksportowane" z powłoki i tym samym importowane do ruby. Jest to przydatne do symulacji/przesłonięcia zachowania w plikach środowiskowych używanych w skryptach init i plikach env systemd.

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 

Teoria działania: Kiedy ruby ​​wysyła przedmiot ENV korzystając p, robi to w taki sposób, że rubin można odczytać z powrotem w jako obiekt. Tak więc używamy powłoki do źródła pliku docelowego i ruby ​​(w podrzędnej powłoce), aby wyprowadzać środowisko w tej postaci szeregowej. Następnie przechwytujemy wyniki ruby ​​i eval w ramach naszego procesu ruby. Oczywiście nie jest to bez ryzyka, więc aby złagodzić ryzyko, (1) sprawdzamy poprawność podanej nazwy pliku i (2) sprawdzamy za pomocą wyrażenia regularnego, że rzecz, którą otrzymujemy z podpowłoki ruby, jest w rzeczywistości serializowany ciąg mieszający. Gdy jesteśmy tego pewni, robimy eval, który tworzy nowy skrót. Następnie "ręcznie" scalamy hash z ENV, który jest Object, a nie zwykłym Hash. Jeśli byłaby to mieszanka, moglibyśmy użyć metody #merge!.

EDYTOWANIE: sh -a wyeksportowane elementy, takie jak PATH. Musimy również usunąć przed scaleniem mieszania SHLVL i PWD.