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 system
ve Open3.popen3
Ruby 1.8 güvenli / kaçan bir varyant sağlamak. Ruby 1.9 IO::popen
da 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, nil
aksi 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.write
ve 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 %x
takma 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
system
Fonksiyon, 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
, capture2e
ve capture3
gibi ç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
, capture2e
ve capture3
ayrıca yakın onları * otomatik s std. (En azından, hiç sorunla karşılaşmadım.)
Open3#popen2
, popen2e
ve 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
system
Aramayı 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#system
aş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