Chociaż to pytanie jest przydatne, nie jest zadawane dobrze. Ruby ma wiele sposobów wywoływania podpowłok, które są dobrze udokumentowane i łatwe do znalezienia poprzez czytanie dokumentacji jądra i Open3 i wyszukiwanie tutaj na SO.
Tin Man
1
Niestety ten temat jest dość złożony. Open3( docs ) jest najlepszym wyborem dla większości sytuacji, IMO, ale w starszych wersjach Ruby nie będzie respektował zmodyfikowanej PATH( bugs.ruby-lang.org/issues/8004 ), i w zależności od tego, jak przekazujesz argumenty (w szczególności , jeśli użyjesz skrótu opcji bez słów kluczowych), może się zepsuć. Ale jeśli trafisz w te sytuacje, robisz coś dość zaawansowanego i możesz dowiedzieć się, co zrobić, czytając implementację Open3.
Joshua Cheek
3
Dziwi mnie, że nikt nie wspomniał Shellwords.escape( doc ). Nie chcesz wstawiać danych wprowadzanych przez użytkownika bezpośrednio do poleceń powłoki - najpierw ucieknij! Zobacz także polecenie wtrysku .
Kelvin,
Odpowiedzi:
1319
To wyjaśnienie opiera się na skomentowanym skrypcie Ruby od mojego znajomego. Jeśli chcesz ulepszyć skrypt, możesz go zaktualizować pod linkiem.
Po pierwsze, zauważ, że gdy Ruby woła powłokę, zwykle wywołuje /bin/sh, a nie Bash. Niektóre składnie Bash nie są obsługiwane przez /bin/shwszystkie systemy.
Oto sposoby wykonania skryptu powłoki:
cmd ="echo 'hi'"# Sample string that can be used
Kernel#` , powszechnie nazywane backticks - `cmd`
To jest jak wiele innych języków, w tym Bash, PHP i Perl.
Zwraca wynik (tj. Standardowe wyjście) polecenia powłoki.
Po xznaku znajduje się separator, którym może być dowolny znak. Jeśli separator jest jednym z bohaterów (, [, {, lub <, dosłowne składa się ze znaków aż do ogranicznika dopasowanie zamknięcia, z uwzględnieniem zagnieżdżonych par ogranicznik. Dla wszystkich innych ograniczników literał obejmuje znaki aż do następnego wystąpienia znaku ogranicznika. Interpolacja ciągów #{ ... }jest dozwolona.
Zwraca wynik (tj. Standardowe wyjście) polecenia powłoki, podobnie jak backsticks.
exec("echo 'hi'")exec( cmd )# Note: this will never be reached because of the line above
Oto kilka dodatkowych porad:
$?które są takie same jak $CHILD_STATUS, uzyskują dostęp do statusu ostatniego wykonanego polecenia systemowego, jeśli używasz backsticks, system()lub %x{}. Następnie możesz uzyskać dostęp do właściwości exitstatusi pid:
Muszę zarejestrować wyniki mojego pliku wykonywalnego na serwerze produkcyjnym, ale nie znalazłem sposobu. Użyłem puts #{cmd}i logger.info ( #{cmd}). Czy jest jakiś sposób na rejestrowanie produkcji w produkcji?
Dla kompletności (jak początkowo myślałem, będzie to również polecenie Ruby): Rake ma sh, który wykonuje polecenie „Uruchom polecenie systemowe cmd. Jeśli podano wiele argumentów, polecenie nie jest uruchamiane z powłoką (taka sama semantyka jak jądro :: exec i Kernel :: system) ".
sschuberth
40
Backticks domyślnie nie przechwytują STDERR. Dołącz `2> i 1` do dowodzenia, jeśli chcesz schwytać
Andrei Botalov
14
Myślę, że ta odpowiedź byłaby nieco ulepszona, gdyby powiedziała, że backsticks i% x zwróciły „wynik”, a nie „wynik” danego polecenia. Ten ostatni można pomylić ze statusem wyjścia. Czy to tylko ja?
Łał haha. Bardzo przydatne, chociaż fakt, że to musi istnieć, jest niefortunne
Josh Bodah,
Na marginesie, uważam, że metoda spawn () znajduje się w wielu różnych miejscach (np. KernelI Processjest najbardziej wszechstronna. Jest mniej więcej taka sama PTY.spawn(), ale bardziej ogólna.
Smar
160
Lubię to robić, używając %xliterału, co ułatwia (i czyta!) Używanie cudzysłowów w poleceniu, tak jak poniżej:
directorylist =%x[find .-name '*test.rb'| sort]
Które w tym przypadku zapełni listę plików wszystkimi plikami testowymi w bieżącym katalogu, które można przetwarzać zgodnie z oczekiwaniami:
directorylist.each do|filename|
filename.chomp!# work with fileend
powyższe nie działa dla mnie. `` <main> ': niezdefiniowana metoda, each' for :String (NoMethodError) jak to dla ciebie działało? Korzystam z ruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]Czy na pewno zwracana jest tablica z polecenia, aby pętla faktycznie działała?
Czy jest jakaś dokumentacja na temat przeprowadzania testów Spec i testów jednostkowych z Open3 lub innymi Open'ami w Ruby std-lib? Na obecnym poziomie zrozumienia trudno mi przetestować powłoki zewnętrzne.
FilBot3,
29
Wybierając między tymi mechanizmami, należy rozważyć następujące rzeczy:
Czy chcesz po prostu stdout, czy też potrzebujesz stderr? A nawet wyodrębniony?
Jak duży jest twój wynik? Czy chcesz zachować cały wynik w pamięci?
Czy chcesz przeczytać niektóre dane wyjściowe, gdy podproces jest nadal uruchomiony?
Potrzebujesz kodów wynikowych?
Czy potrzebujesz obiektu Ruby, który reprezentuje proces i pozwala zabijać go na żądanie?
Może trzeba coś od prostych backticks ( ``) system(), a IO.popendo pełnowymiarową Kernel.fork/ Kernel.execz IO.pipei IO.select.
Możesz także wrzucić limity czasu do miksu, jeśli podproces trwa zbyt długo.
Jeśli naprawdę potrzebujesz Bash, zgodnie z notatką w „najlepszej” odpowiedzi.
Po pierwsze, zauważ, że gdy Ruby woła powłokę, zwykle wywołuje /bin/sh, a nie Bash. Niektóre składnie Bash nie są obsługiwane przez /bin/shwszystkie systemy.
Za pomocą Kernel#systempolecenia zwraca, truejeśli się powiedzie, falsejeśli się nie powiedzie i zwraca, niljeśli wykonanie polecenia się nie powiedzie:
Korzystając z odpowiedzi tutaj i powiązanych w odpowiedzi Mihai, stworzyłem funkcję, która spełnia następujące wymagania:
Starannie rejestruje STDOUT i STDERR, aby nie „wyciekły”, gdy mój skrypt jest uruchamiany z konsoli.
Umożliwia przekazywanie argumentów do powłoki jako tablicy, więc nie musisz się martwić o ucieczkę.
Przechwytuje status wyjścia polecenia, aby było jasne, kiedy wystąpił błąd.
Jako bonus, ten zwróci również STDOUT w przypadkach, gdy polecenie powłoki zakończy się pomyślnie (0) i umieści cokolwiek w STDOUT. W ten sposób różni się od system, co truew takich przypadkach po prostu zwraca .
Kod następuje. Szczególną funkcją jest system_quietly:
require'open3'classShellError<StandardError;end#actual function:def system_quietly(*cmd)
exit_status=nil
err=nilout=nilOpen3.popen3(*cmd)do|stdin, stdout, stderr, wait_thread|
err = stderr.gets(nil)out= stdout.gets(nil)[stdin, stdout, stderr].each{|stream| stream.send('close')}
exit_status = wait_thread.valueendif exit_status.to_i >0
err = err.chomp if err
raiseShellError, err
elsifoutreturnout.chomp
elsereturntrueendend#calling it:begin
puts system_quietly('which','ruby')rescueShellError
abort "Looks like you don't have the `ruby` command. Odd."end#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"
Nie zapomnij spawnpolecenia utworzenia procesu w tle w celu wykonania określonego polecenia. Możesz nawet poczekać na jej zakończenie, korzystając z Processklasy i zwróconych pid:
Kernel.spawn()wydaje się być znacznie bardziej wszechstronny niż wszystkie inne opcje.
Kashyap
6
Jeśli masz bardziej złożony przypadek niż zwykły przypadek, z którym nie można sobie poradzić ``, sprawdź Kernel.spawn(). Wydaje się, że jest to najbardziej ogólny / w pełni funkcjonalny program Ruby do wykonywania poleceń zewnętrznych.
Możesz go użyć do:
utwórz grupy procesów (Windows).
przekierowanie do, wyjście, błąd do plików / siebie.
ustaw env vars, umask.
zmień katalog przed wykonaniem polecenia.
ustawić limity zasobów dla procesora / danych / itp.
Zrób wszystko, co można zrobić z innymi opcjami w innych odpowiedziach, ale z większym kodem.
Dokumentacja Ruby ma wystarczająco dobrych przykładów:
env: hash
name => val :set the environment variable
name =>nil: unset the environment variable
command...:
commandline : command line string which is passed to the standard shell
cmdname, arg1,...: command name and one or more arguments (no shell)[cmdname, argv0], arg1,...: command name, argv[0]and zero or more arguments (no shell)
options: hash
clearing environment variables::unsetenv_others =>true: clear environment variables except specified by env
:unsetenv_others =>false: dont clear (default)
process group::pgroup =>trueor0: make a new process group:pgroup => pgid :join to specified process group:pgroup =>nil: dont change the process group(default)
create new process group:Windows only
:new_pgroup =>true: the new process is the root process of a new process group:new_pgroup =>false: dont create a new process group(default)
resource limit: resourcename is core, cpu, data, etc.SeeProcess.setrlimit.:rlimit_resourcename => limit
:rlimit_resourcename =>[cur_limit, max_limit]
current directory::chdir => str
umask::umask =>int
redirection:
key:
FD : single file descriptor in child process
[FD, FD,...]: multiple file descriptor in child process
value:
FD : redirect to the file descriptor in parent process
string: redirect to file with open(string,"r"or"w")[string]: redirect to file with open(string,File::RDONLY)[string, open_mode]: redirect to file with open(string, open_mode,0644)[string, open_mode, perm]: redirect to file with open(string, open_mode, perm)[:child, FD]: redirect to the redirected file descriptor
:close : close the file descriptor in child process
FD is one of follows
:in: the file descriptor 0 which is the standard input
:out: the file descriptor 1 which is the standard output
:err : the file descriptor 2 which is the standard error
integer : the file descriptor of specified the integer
io : the file descriptor specified as io.fileno
file descriptor inheritance: close non-redirected non-standard fds (3,4,5,...)ornot:close_others =>false: inherit fds (defaultfor system andexec):close_others =>true: dont inherit (defaultfor spawn and IO.popen)
require'open3'
a="attrib"Open3.popen3(a)do|stdin, stdout, stderr|
puts stdout.read
end
Przekonałem się, że chociaż ta metoda nie jest tak niezapomniana jak
system("thecommand")
lub
`thecommand`
w backticks, dobrą rzeczą w tej metodzie w porównaniu z innymi metodami jest to, że backticks nie pozwalają mi putsna polecenie, które wykonuję / przechowuję polecenie, które chcę uruchomić w zmiennej, i system("thecommand")nie wydaje mi się, aby uzyskać wynik, podczas gdy ta metoda pozwala mi robić obie te rzeczy i umożliwia mi niezależny dostęp do stdin, stdout i stderr.
Oto fajny, którego używam w skrypcie ruby w systemie OS X (dzięki czemu mogę uruchomić skrypt i uzyskać aktualizację nawet po przełączeniu się z okna):
cmd =%Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Open3
( docs ) jest najlepszym wyborem dla większości sytuacji, IMO, ale w starszych wersjach Ruby nie będzie respektował zmodyfikowanejPATH
( bugs.ruby-lang.org/issues/8004 ), i w zależności od tego, jak przekazujesz argumenty (w szczególności , jeśli użyjesz skrótu opcji bez słów kluczowych), może się zepsuć. Ale jeśli trafisz w te sytuacje, robisz coś dość zaawansowanego i możesz dowiedzieć się, co zrobić, czytając implementacjęOpen3
.Shellwords.escape
( doc ). Nie chcesz wstawiać danych wprowadzanych przez użytkownika bezpośrednio do poleceń powłoki - najpierw ucieknij! Zobacz także polecenie wtrysku .Odpowiedzi:
To wyjaśnienie opiera się na skomentowanym skrypcie Ruby od mojego znajomego. Jeśli chcesz ulepszyć skrypt, możesz go zaktualizować pod linkiem.
Po pierwsze, zauważ, że gdy Ruby woła powłokę, zwykle wywołuje
/bin/sh
, a nie Bash. Niektóre składnie Bash nie są obsługiwane przez/bin/sh
wszystkie systemy.Oto sposoby wykonania skryptu powłoki:
Kernel#`
, powszechnie nazywane backticks -`cmd`
To jest jak wiele innych języków, w tym Bash, PHP i Perl.
Zwraca wynik (tj. Standardowe wyjście) polecenia powłoki.
Dokumenty: http://ruby-doc.org/core/Kernel.html#method-i-60
Wbudowana składnia,
%x( cmd )
Po
x
znaku znajduje się separator, którym może być dowolny znak. Jeśli separator jest jednym z bohaterów(
,[
,{
, lub<
, dosłowne składa się ze znaków aż do ogranicznika dopasowanie zamknięcia, z uwzględnieniem zagnieżdżonych par ogranicznik. Dla wszystkich innych ograniczników literał obejmuje znaki aż do następnego wystąpienia znaku ogranicznika. Interpolacja ciągów#{ ... }
jest dozwolona.Zwraca wynik (tj. Standardowe wyjście) polecenia powłoki, podobnie jak backsticks.
Dokumenty: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings
Kernel#system
Wykonuje podane polecenie w podpowłoce.
Zwraca,
true
jeśli polecenie zostało znalezione i uruchomione pomyślnie, wfalse
przeciwnym razie.Dokumenty: http://ruby-doc.org/core/Kernel.html#method-i-system
Kernel#exec
Zastępuje bieżący proces, uruchamiając dane polecenie zewnętrzne.
Nie zwraca żadnych, aktualny proces jest zastępowany i nigdy nie jest kontynuowany.
Dokumenty: http://ruby-doc.org/core/Kernel.html#method-i-exec
Oto kilka dodatkowych porad:
$?
które są takie same jak$CHILD_STATUS
, uzyskują dostęp do statusu ostatniego wykonanego polecenia systemowego, jeśli używasz backsticks,system()
lub%x{}
. Następnie możesz uzyskać dostęp do właściwościexitstatus
ipid
:Więcej informacji można znaleźć w:
źródło
#{cmd}
i logger.info (#{cmd}
). Czy jest jakiś sposób na rejestrowanie produkcji w produkcji?cmd
. Jeśli podano wiele argumentów, polecenie nie jest uruchamiane z powłoką (taka sama semantyka jak jądro :: exec i Kernel :: system) ".Oto schemat blokowy oparty na „ Kiedy używać każdej metody uruchamiania podprocesu w Rubim ”. Zobacz także: „ Oszukuj aplikację, aby pomyślała, że jej standardowe wyjście to terminal, a nie potok ”.
źródło
Kernel
IProcess
jest najbardziej wszechstronna. Jest mniej więcej taka samaPTY.spawn()
, ale bardziej ogólna.Lubię to robić, używając
%x
literału, co ułatwia (i czyta!) Używanie cudzysłowów w poleceniu, tak jak poniżej:Które w tym przypadku zapełni listę plików wszystkimi plikami testowymi w bieżącym katalogu, które można przetwarzać zgodnie z oczekiwaniami:
źródło
%x[ cmd ]
zwraca tablicę?each' for :String (NoMethodError)
jak to dla ciebie działało? Korzystam zruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]
Czy na pewno zwracana jest tablica z polecenia, aby pętla faktycznie działała?Oto najlepszy artykuł, moim zdaniem, na temat uruchamiania skryptów powłoki w Rubim: „ 6 sposobów uruchamiania poleceń powłoki w Rubim ”.
Jeśli potrzebujesz tylko wyjścia, użyj backsticksa.
Potrzebowałem bardziej zaawansowanych rzeczy, takich jak STDOUT i STDERR, więc użyłem klejnotu Open4. Wszystkie metody zostały tam wyjaśnione.
źródło
%x
opcji składni.spawn
metody.Moim ulubionym jest Open3
źródło
stdout, stderr, status = Open3.capture3('nroff -man', :stdin_data => stdin)
Wybierając między tymi mechanizmami, należy rozważyć następujące rzeczy:
Może trzeba coś od prostych backticks ( ``)
system()
, aIO.popen
do pełnowymiarowąKernel.fork
/Kernel.exec
zIO.pipe
iIO.select
.Możesz także wrzucić limity czasu do miksu, jeśli podproces trwa zbyt długo.
Niestety, to bardzo zależy .
źródło
Jeszcze jedna opcja:
Kiedy ty:
Możesz użyć przekierowania powłoki:
2>&1
Składnia działa na Linux , Mac i systemu Windows od wczesnych dni od MS-DOS.źródło
Zdecydowanie nie jestem ekspertem od Ruby, ale spróbuję:
Powinieneś być także w stanie robić takie rzeczy jak:
źródło
Powyższe odpowiedzi są już dość świetne, ale naprawdę chcę udostępnić następujący artykuł podsumowujący: „ 6 sposobów uruchamiania poleceń powłoki w Rubim ”
Zasadniczo mówi nam:
Kernel#exec
:system
i$?
:Backticks (`):
IO#popen
:Open3#popen3
- stdlib:Open4#popen4
-- klejnot:źródło
Jeśli naprawdę potrzebujesz Bash, zgodnie z notatką w „najlepszej” odpowiedzi.
Jeśli chcesz użyć Bash, wstaw
bash -c "your Bash-only command"
żądaną metodę wywoływania:Testować:
Lub jeśli działasz istniejący plik skryptu, taki jak
Ruby powinna uszanować shebang, ale zawsze możesz go użyć
dla pewności, chociaż
/bin/sh
bieg może być niewielki/bin/bash
, prawdopodobnie tego nie zauważysz.źródło
Możesz także użyć operatorów wstecznych (`), podobnych do Perla:
Przydatne, jeśli potrzebujesz czegoś prostego.
Wybór metody zależy dokładnie od tego, co próbujesz osiągnąć; sprawdź dokumentację, aby uzyskać więcej informacji na temat różnych metod.
źródło
Możemy to osiągnąć na wiele sposobów.
Używanie
Kernel#exec
, nic po wykonaniu tego polecenia:Za pomocą
backticks or %x
Za pomocą
Kernel#system
polecenia zwraca,true
jeśli się powiedzie,false
jeśli się nie powiedzie i zwraca,nil
jeśli wykonanie polecenia się nie powiedzie:źródło
Najprostszym sposobem jest na przykład:
źródło
Korzystając z odpowiedzi tutaj i powiązanych w odpowiedzi Mihai, stworzyłem funkcję, która spełnia następujące wymagania:
Jako bonus, ten zwróci również STDOUT w przypadkach, gdy polecenie powłoki zakończy się pomyślnie (0) i umieści cokolwiek w STDOUT. W ten sposób różni się od
system
, cotrue
w takich przypadkach po prostu zwraca .Kod następuje. Szczególną funkcją jest
system_quietly
:źródło
Nie zapomnij
spawn
polecenia utworzenia procesu w tle w celu wykonania określonego polecenia. Możesz nawet poczekać na jej zakończenie, korzystając zProcess
klasy i zwróconychpid
:Dokument mówi: Ta metoda jest podobna do tej,
#system
ale nie czeka na zakończenie polecenia.źródło
Kernel.spawn()
wydaje się być znacznie bardziej wszechstronny niż wszystkie inne opcje.Jeśli masz bardziej złożony przypadek niż zwykły przypadek, z którym nie można sobie poradzić
``
, sprawdźKernel.spawn()
. Wydaje się, że jest to najbardziej ogólny / w pełni funkcjonalny program Ruby do wykonywania poleceń zewnętrznych.Możesz go użyć do:
Dokumentacja Ruby ma wystarczająco dobrych przykładów:
źródło
Metoda backticks (`) jest najłatwiejszą do wywoływania poleceń powłoki z Ruby. Zwraca wynik polecenia powłoki:
źródło
Biorąc pod uwagę polecenie
attrib
:Przekonałem się, że chociaż ta metoda nie jest tak niezapomniana jak
lub
w backticks, dobrą rzeczą w tej metodzie w porównaniu z innymi metodami jest to, że backticks nie pozwalają mi
puts
na polecenie, które wykonuję / przechowuję polecenie, które chcę uruchomić w zmiennej, isystem("thecommand")
nie wydaje mi się, aby uzyskać wynik, podczas gdy ta metoda pozwala mi robić obie te rzeczy i umożliwia mi niezależny dostęp do stdin, stdout i stderr.Zobacz „ Wykonywanie poleceń w Rubim ” i dokumentację Open3 Ruby .
źródło
To nie jest tak naprawdę odpowiedź, ale może ktoś uzna ją za przydatną:
Kiedy używasz TK GUI w systemie Windows i musisz wywoływać polecenia powłoki z rubyw, zawsze pojawi się denerwujące okno CMD na mniej niż sekundę.
Aby tego uniknąć, możesz użyć:
lub
Oba będą przechowywać dane
ipconfig
wyjściowe w środkulog.txt
, ale nie pojawią się żadne okna.Musisz zajrzeć do
require 'win32ole'
skryptu.system()
,exec()
ispawn()
wszystkie pojawią się w tym irytującym oknie podczas korzystania z TK i rubyw.źródło
Oto fajny, którego używam w skrypcie ruby w systemie OS X (dzięki czemu mogę uruchomić skrypt i uzyskać aktualizację nawet po przełączeniu się z okna):
źródło