Ruby'de Kernel # sistemini kullanarak bir komut çağırırsam , çıktısını nasıl alabilirim?
system("ls")
Ruby'de Kernel # sistemini kullanarak bir komut çağırırsam , çıktısını nasıl alabilirim?
system("ls")
Yanıtlar:
Kaosun cevabını biraz genişletmek ve açıklığa kavuşturmak istiyorum .
Komutunuzu geri tıklamayla kuşatırsanız, (açıkça) system () öğesini çağırmanıza gerek yoktur. Backticks komutu yürütür ve çıktıyı dize olarak döndürür. Daha sonra değeri şöyle bir değişkene atayabilirsiniz:
output = `ls`
p output
veya
printf output # escapes newline chars
ls #{filename}.
command 2>&1
Eğer kullanıcı sağlanan değerleri içeren bir dize geçmesi tüm çözümler unutmayın system, %x[]vb güvensiz! Güvenli olmayan aslında şu anlama gelir: kullanıcı, bağlamda ve programın tüm izinleriyle çalıştırmak için kodu tetikleyebilir.
Sadece söyleyebilirim systemve Open3.popen3Ruby 1.8 güvenli / kaçan bir varyant sağlamak. Ruby 1.9 IO::popenda bir dizi kabul eder.
Her seçenek ve argümanı bir dizi olarak bu çağrılardan birine geçirmeniz yeterlidir.
Yalnızca çıkış durumuna değil, muhtemelen kullanmak istediğiniz sonuca da ihtiyacınız varsa Open3.popen3:
require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username'])
stdout.gets(nil)
stdout.close
stderr.gets(nil)
stderr.close
exit_code = wait_thr.value
Blok formunun stdin, stdout ve stderr'yi otomatik olarak kapatacağını unutmayın, aksi takdirde açıkça kapatılması gerekir .
Daha fazla bilgi burada: Ruby'de sıhhi kabuk komutları veya sistem çağrıları oluşturma
getsçağrılar argümanı geçmelidir, nilaksi takdirde çıktının ilk satırını alırız. Örneğin stdout.gets(nil);
Open3.popen3önemli bir sorun eksik olduğunu düşünüyorum : Stdout bir boru tutabilir daha fazla veri yazan bir alt işlem varsa, alt işlem askıya alır stderr.writeve program sıkışmış stdout.gets(nil).
Sadece kayıt için, her ikisini de istiyorsanız (çıktı ve işlem sonucu) şunları yapabilirsiniz:
output=`ls no_existing_file` ; result=$?.success?
output=`ls no_existing_file 2>&1`; result=$?.success?
Basit yolu güvenli doğru bunu yapmak, yani kullanmaktır Open3.capture2(), Open3.capture2e()ya Open3.capture3().
Güvenilmeyen verilerle kullanılırsa, ruby'nin ters tırnaklarını ve %xtakma adlarını kullanmak HERHANGİ BİR DURUMDA GÜVENİLİR DEĞİLDİR . Öyle TEHLİKELİ , sade ve basit:
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
systemFonksiyon, aksine, düzgün argümanları kaçar doğru kullanıldığı takdirde :
ret = system "echo #{untrusted}" # BAD
ret = system 'echo', untrusted # good
Sorun, çıkış yerine çıkış kodunu döndürür ve ikincisini yakalamak kıvrımlı ve dağınıktır.
Bu konuda şimdiye kadarki en iyi cevap Open3'ten bahsediyor, ancak görev için en uygun fonksiyonlardan değil. Open3.capture2, capture2eve capture3gibi çalışır system, ancak iki veya üç bağımsız değişken döndürür:
out, err, st = Open3.capture3("echo #{untrusted}") # BAD
out, err, st = Open3.capture3('echo', untrusted) # good
out_err, st = Open3.capture2e('echo', untrusted) # good
out, st = Open3.capture2('echo', untrusted) # good
p st.exitstatus
Başka bir söz IO.popen(). Sözdizimi, bir diziyi girdi olarak istemesi anlamında beceriksiz olabilir, ancak aynı zamanda çalışır:
out = IO.popen(['echo', untrusted]).read # good
Kolaylık sağlamak için Open3.capture3(), örneğin bir işlevi tamamlayabilirsiniz:
#
# Returns stdout on success, false on failure, nil on error
#
def syscall(*cmd)
begin
stdout, stderr, status = Open3.capture3(*cmd)
status.success? && stdout.slice!(0..-(1 + $/.size)) # strip trailing eol
rescue
end
end
Misal:
p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')
Aşağıdakileri verir:
nil
nil
false
false
/usr/bin/which <— stdout from system('which', 'which')
true <- p system('which', 'which')
"/usr/bin/which" <- p syscall('which', 'which')
require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read } Blok formunun stdin, stdout ve stderr'yi otomatik olarak kapatacağını unutmayın; aksi takdirde açıkça kapatılması gerekir .
capture2, capture2eve capture3ayrıca yakın onları * otomatik s std. (En azından, hiç sorunla karşılaşmadım.)
Open3#popen2, popen2eve popen3önceden tanımlanmış bir blokla: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
Ne tür bir sonuca ihtiyacınız olduğuna bağlı olarak system () veya% x [] kullanabilirsiniz.
komut başarılı bir şekilde bulunur ve çalıştırılırsa system () işlevi true değerini, aksi halde false değerini döndürür.
>> s = system 'uptime'
10:56 up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.14
=> true
>> s.class
=> TrueClass
>> $?.class
=> Process::Status
% x [..] ise komutun sonuçlarını dize olarak kaydeder:
>> result = %x[uptime]
=> "13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> p result
"13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> result.class
=> String
Th Jay Fields tarafından blog yazısı [..], exec ve% x ayrıntılı olarak kullanarak sistemi arasındaki farkları açıklar.
Bağımsız değişkenlerden kaçmanız gerekiyorsa, Ruby 1.9'da IO.popen ayrıca bir dizi kabul eder:
p IO.popen(["echo", "it's escaped"]).read
Önceki sürümlerde Open3.popen3'ü kullanabilirsiniz :
require "open3"
Open3.popen3("echo", "it's escaped") { |i, o| p o.read }
Stdin'i de geçmeniz gerekiyorsa, bu hem 1.9 hem de 1.8'de çalışmalıdır:
out = IO.popen("xxd -p", "r+") { |io|
io.print "xyz"
io.close_write
io.read.chomp
}
p out # "78797a"
Backtick kullanıyorsunuz:
`ls`
ruby -e '%x{ls}'- not, çıktı yok. ( %x{}
shçıktıyı konsola (yani STDOUT) yankılayıp geri döndürür. Bu olmaz.
Başka bir yol:
f = open("|ls")
foo = f.read()
Not "boru" karakteri önce "ls" açık. Bu aynı zamanda program çıktısına veri beslemenin yanı sıra standart çıktısını okumak için de kullanılabilir.
Dönüş değerine ihtiyacınız varsa aşağıdakilerin yararlı olduğunu gördüm:
result = %x[ls]
puts result
Özellikle makinemdeki tüm Java işlemlerinin pidelerini listelemek istedim ve bunu kullandım:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
As Simon Hürlimann zaten açıkladı , Open3 backticks vb daha güvenlidir
require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
Blok formunun stdin, stdout ve stderr'yi otomatik olarak kapatacağını unutmayın, aksi takdirde açıkça kapatılması gerekir .
Sıkıştırma veya popen kullanmak genellikle istediğiniz şey olsa da, aslında sorulan soruya cevap vermez. systemÇıktı yakalamanın geçerli nedenleri olabilir (belki otomatik test için). Küçük bir Google , başkalarının yararına burada yayınlayacağımı düşündüğüm bir cevap verdi .
Test etmek için buna ihtiyaç duyduğumdan system, örneğim , gerçek çağrı test edilen koda gömülü olduğundan standart çıktıyı yakalamak için bir blok kurulumu kullanır :
require 'tempfile'
def capture_stdout
stdout = $stdout.dup
Tempfile.open 'stdout-redirect' do |temp|
$stdout.reopen temp.path, 'w+'
yield if block_given?
$stdout.reopen stdout
temp.read
end
end
Bu yöntem, gerçek verileri saklamak için bir geçici dosya kullanarak verilen bloktaki herhangi bir çıktıyı yakalar. Örnek kullanım:
captured_content = capture_stdout do
system 'echo foo'
end
puts captured_content
systemAramayı dahili olarak çağıran herhangi bir şeyle değiştirebilirsiniz system. İsterseniz yakalamak için benzer bir yöntem de kullanabilirsiniz stderr.
Çıktının bir dosyaya yeniden yönlendirilmesini istiyorsanız, Kernel#systemaşağıdaki gibi tanımlayıcıları değiştirebilirsiniz:
stdout ve stderr'i ekleme modunda bir dosyaya (/ tmp / log) yönlendirin:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
Uzun süren bir komut için, çıktı gerçek zamanlı olarak saklanır. Ayrıca, çıktıyı bir IO.pipe kullanarak depolayabilir ve Çekirdek # sisteminden yeniden yönlendirebilirsiniz.
Doğrudan sistem (...) yerine Open3.popen3 (...) kullanabilirsiniz.
Daha fazla tartışma: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
Bunu burada bulamadım, bu yüzden ekleyerek, tam çıktıyı almakta bazı sorunlar yaşadım.
STDERR'ı backtick kullanarak yakalamak istiyorsanız STDERR'ı STDOUT'a yönlendirebilirsiniz.
output = `grep sunucuları / private / etc / * 2> & 1`
kaynak: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
puts `date`
puts $?
Mon Mar 7 19:01:15 PST 2016
pid 13093 exit 0