ถ้าฉันเรียกคำสั่งโดยใช้Kernel # systemใน Ruby ฉันจะรับเอาต์พุตได้อย่างไร
system("ls")
ถ้าฉันเรียกคำสั่งโดยใช้Kernel # systemใน Ruby ฉันจะรับเอาต์พุตได้อย่างไร
system("ls")
คำตอบ:
ฉันต้องการที่จะขยายและชี้แจงคำตอบของความสับสนวุ่นวายเล็กน้อย
หากคุณล้อมรอบคำสั่งของคุณด้วย backticks แสดงว่าคุณไม่จำเป็นต้องมีระบบการโทร (อย่างชัดเจน) () เลย backticks ดำเนินการคำสั่งและส่งกลับผลลัพธ์เป็นสตริง จากนั้นคุณสามารถกำหนดค่าให้กับตัวแปรดังนี้:
output = `ls`
p output
หรือ
printf output # escapes newline chars
ls #{filename}
.
command 2>&1
โปรดทราบว่าการแก้ปัญหาทั้งหมดที่คุณผ่านสตริงที่มีค่าให้กับผู้ใช้system
, %x[]
ฯลฯ ที่ไม่ปลอดภัย! จริง ๆ แล้วไม่ปลอดภัยหมายถึง: ผู้ใช้อาจเรียกใช้รหัสเพื่อเรียกใช้ในบริบทและด้วยสิทธิ์ทั้งหมดของโปรแกรม
เท่าที่ฉันสามารถพูดได้เท่านั้นsystem
และOpen3.popen3
มีตัวแปรความปลอดภัย / การหลบหนีใน Ruby 1.8 ใน Ruby 1.9 IO::popen
ยังยอมรับอาร์เรย์
เพียงส่งผ่านตัวเลือกและอาร์กิวเมนต์เป็นอาร์เรย์ไปยังหนึ่งในการโทรเหล่านี้
หากคุณต้องการไม่ใช่เพียงแค่สถานะออก แต่ยังเป็นผลลัพธ์ที่คุณอาจต้องการใช้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
หมายเหตุว่ารูปแบบบล็อกจะปิดอัตโนมัติ stdin, stdout และ stderr- มิฉะนั้นพวกเขาจะต้องถูกปิดอย่างชัดเจน
ข้อมูลเพิ่มเติมที่นี่: การสร้างคำสั่งอนามัยเชลล์หรือการเรียกระบบใน Ruby
gets
เรียกควรผ่านการโต้แย้งnil
เช่นเดียวกับที่เราเพิ่งได้รับบรรทัดแรกของผลลัพธ์ stdout.gets(nil)
ดังนั้นเช่น
Open3.popen3
จะหายไปเป็นปัญหาใหญ่: ถ้าคุณมีกระบวนการย่อยที่เขียนข้อมูลเพิ่มเติมเพื่อ stdout กว่าท่อสามารถถือเป็นกระบวนการย่อยได้รับการระงับในและโปรแกรมของคุณได้รับในการติดstderr.write
stdout.gets(nil)
เพียงบันทึกถ้าคุณต้องการทั้ง (เอาท์พุทและผลการดำเนินงาน) คุณสามารถทำได้:
output=`ls no_existing_file` ; result=$?.success?
output=`ls no_existing_file 2>&1`; result=$?.success?
วิธีที่ตรงไปตรงมาจะทำเช่นนี้ได้อย่างถูกต้องและปลอดภัยคือการใช้งานOpen3.capture2()
, Open3.capture2e()
หรือOpen3.capture3()
หรือ
การใช้ backticks ของ ruby และ%x
alias นั้นไม่ปลอดภัยภายใต้สถานการณ์ใด ๆหากใช้กับข้อมูลที่ไม่น่าเชื่อถือ มันเป็นอันตรายธรรมดาและเรียบง่าย:
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
system
ฟังก์ชั่นในทางตรงกันข้ามหนีข้อโต้แย้งอย่างถูกต้องถ้าใช้อย่างถูกต้อง :
ret = system "echo #{untrusted}" # BAD
ret = system 'echo', untrusted # good
ปัญหาคือมันส่งคืนรหัสออกแทนผลลัพธ์และการจับภาพหลังนั้นซับซ้อนและยุ่งเหยิง
คำตอบที่ดีที่สุดในหัวข้อนี้กล่าวถึง Open3 แต่ไม่ใช่ฟังก์ชั่นที่เหมาะสมที่สุดสำหรับงาน Open3.capture2
, capture2e
และcapture3
การทำงานเหมือนsystem
แต่ผลตอบแทนที่สองหรือสามข้อโต้แย้ง:
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
อีกเรื่องที่กล่าวIO.popen()
มา ไวยากรณ์สามารถเงอะงะในแง่ที่ว่ามันต้องการอาร์เรย์เป็นอินพุท แต่ก็ใช้งานได้เช่นกัน:
out = IO.popen(['echo', untrusted]).read # good
เพื่อความสะดวกคุณสามารถห่อOpen3.capture3()
ในฟังก์ชั่นเช่น:
#
# 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
ตัวอย่าง:
p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')
ให้ผลตอบแทนต่อไปนี้:
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 }
หมายเหตุว่ารูปแบบบล็อกจะปิดอัตโนมัติ stdin, stdout และ stderr- มิฉะนั้นพวกเขาจะต้องถูกปิดอย่างชัดเจน
capture2
, capture2e
และcapture3
ยังอยู่ใกล้พวกเขา std * s โดยอัตโนมัติ (อย่างน้อยที่สุดฉันไม่เคยพบปัญหาในตอนท้ายของฉัน)
Open3#popen2
, popen2e
และpopen3
มีบล็อกที่กำหนดไว้ล่วงหน้า: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
คุณสามารถใช้ระบบ () หรือ% x [] ขึ้นอยู่กับผลลัพธ์ที่คุณต้องการ
ระบบ () กลับจริงถ้าคำสั่งพบและวิ่งได้สำเร็จเป็นเท็จอย่างอื่น
>> 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 [.. ] ในทางกลับกันจะบันทึกผลลัพธ์ของคำสั่งเป็นสตริง:
>> 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 บล็อกโพสต์โดยเจทุ่งอธิบายรายละเอียดความแตกต่างระหว่างการใช้ระบบบริหารและ% x [ .. ]
หากคุณต้องการหลีกเลี่ยงการขัดแย้งใน Ruby 1.9 IO.popenก็ยอมรับอาร์เรย์เช่นกัน
p IO.popen(["echo", "it's escaped"]).read
ในเวอร์ชันก่อนหน้าคุณสามารถใช้Open3.popen3 :
require "open3"
Open3.popen3("echo", "it's escaped") { |i, o| p o.read }
หากคุณต้องผ่านการ stdin สิ่งนี้ควรใช้ได้ทั้ง 1.9 และ 1.8:
out = IO.popen("xxd -p", "r+") { |io|
io.print "xyz"
io.close_write
io.read.chomp
}
p out # "78797a"
คุณใช้ backticks:
`ls`
ruby -e '%x{ls}'
- หมายเหตุไม่มีผลลัพธ์ (fyi %x{}
เทียบเท่ากับ backticks)
sh
จะสะท้อนเอาต์พุตไปยังคอนโซล (เช่น STDOUT) และส่งคืน สิ่งนี้ไม่ได้
อีกวิธีคือ:
f = open("|ls")
foo = f.read()
โปรดทราบว่าเป็นอักขระ "ไปป์" ก่อน "ls" ในการเปิด นอกจากนี้ยังสามารถใช้เพื่อป้อนข้อมูลลงในอินพุตมาตรฐานของโปรแกรมรวมทั้งอ่านเอาต์พุตมาตรฐาน
ฉันพบว่าข้อมูลต่อไปนี้มีประโยชน์หากคุณต้องการค่าส่งคืน:
result = %x[ls]
puts result
ฉันต้องการแสดงรายการ pids ของกระบวนการ Java ทั้งหมดในเครื่องของฉันและใช้สิ่งนี้:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
ขณะที่ไซมอนHürlimannอธิบายแล้ว , Open3ปลอดภัยกว่า backticks ฯลฯ
require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
หมายเหตุว่ารูปแบบบล็อกจะปิดอัตโนมัติ stdin, stdout และ stderr- มิฉะนั้นพวกเขาจะต้องถูกปิดอย่างชัดเจน
ในขณะที่ใช้ backticks หรือ popen มักเป็นสิ่งที่คุณต้องการ แต่ก็ไม่ได้ตอบคำถามที่ถาม อาจมีเหตุผลที่ถูกต้องสำหรับการบันทึกsystem
ผลลัพธ์ (อาจใช้สำหรับการทดสอบอัตโนมัติ) Googling เล็กน้อยตอบคำถามฉันคิดว่าฉันจะโพสต์ที่นี่เพื่อประโยชน์ของผู้อื่น
เนื่องจากฉันต้องการสิ่งนี้สำหรับการทดสอบตัวอย่างของฉันใช้การตั้งค่าบล็อกเพื่อจับเอาท์พุทมาตรฐานเนื่องจากการsystem
โทรจริงถูกฝังอยู่ในรหัสที่กำลังทดสอบ:
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
เมธอดนี้รวบรวมเอาต์พุตใด ๆ ในบล็อกที่กำหนดโดยใช้ tempfile เพื่อเก็บข้อมูลจริง ตัวอย่างการใช้งาน:
captured_content = capture_stdout do
system 'echo foo'
end
puts captured_content
คุณสามารถเปลี่ยนการโทรที่มีสิ่งที่เรียกร้องภายในsystem
system
คุณสามารถใช้วิธีการที่คล้ายกันเพื่อจับภาพstderr
หากคุณต้องการ
หากคุณต้องการให้ผลลัพธ์เปลี่ยนเส้นทางไปยังไฟล์โดยใช้Kernel#system
คุณสามารถแก้ไข descriptor ดังนี้:
เปลี่ยนเส้นทาง stdout และ stderr ไปยังไฟล์ (/ tmp / log) ในโหมดต่อท้าย:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
สำหรับคำสั่งที่รันนานสิ่งนี้จะเก็บเอาท์พุทแบบเรียลไทม์ คุณสามารถเก็บเอาท์พุทโดยใช้ IO.pipe และเปลี่ยนเส้นทางจากระบบเคอร์เนล
ในฐานะที่เป็นระบบโดยตรง (... ) คุณสามารถใช้ Open3.popen3 (... ) แทนได้
การอภิปรายเพิ่มเติม: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
ฉันไม่พบอันนี้ที่นี่เพื่อเพิ่มเข้าไปฉันมีปัญหาบางอย่างในการรับเอาต์พุตเต็มรูปแบบ
คุณสามารถเปลี่ยนเส้นทาง STDERR ไปยัง STDOUT หากคุณต้องการจับภาพ STDERR โดยใช้ backtick
output = `grep hosts / private / etc / * 2> & 1`
แหล่งที่มา: 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