Sé que este es un hilo antiguo, pero hay otra opción que fue ligeramente tocada por Simon Hürlimann.
No hay mucha información sobre este tema y creo que esto podría ayudar a otros que lo necesiten.
Para este ejemplo vamos a utilizar Open3
que le da la capacidad de ejecutar órdenes sincrónica o asincrónica, y proporciona la salida estándar , stderr, códigos de salida, y PID.
Open3 le otorga acceso a stdout, stderr, códigos de salida y un hilo para esperar el proceso secundario cuando se ejecuta otro programa. Puede especificar varios atributos, redirecciones, directorio actual, etc., del programa de la misma manera que para Process.spawn. (Fuente: Open3 Docs)
me eligió para dar formato a la salida como un objeto CommandStatus
. Esto contiene nuestro stdout
, stderr
, pid
(del hilo de trabajo) y exitstatus
.
class Command
require 'open3'
class CommandStatus
@stdout = nil
@stderr = nil
@pid = nil
@exitstatus = nil
def initialize(stdout, stderr, process)
@stdout = stdout
@stderr = stderr
@pid = process.pid
@exitstatus = process.exitstatus
end
def stdout
@stdout
end
def stderr
@stderr
end
def exit_status
@exitstatus
end
def pid
@pid
end
end
def self.execute(command)
command_stdout = nil
command_stderr = nil
process = Open3.popen3(ENV, command + ';') do |stdin, stdout, stderr, thread|
stdin.close
stdout_buffer = stdout.read
stderr_buffer = stderr.read
command_stdout = stdout_buffer if stdout_buffer.length > 0
command_stderr = stderr_buffer if stderr_buffer.length > 0
thread.value # Wait for Process::Status object to be returned
end
return CommandStatus.new(command_stdout, command_stderr, process)
end
end
cmd = Command::execute("echo {1..10}")
puts "STDOUT: #{cmd.stdout}"
puts "STDERR: #{cmd.stderr}"
puts "EXIT: #{cmd.exit_status}"
Durante la lectura de las memorias intermedias STDOUT/ERR, utilizo command_stdout = stdout_buffer if stdout_buffer.length > 0
para controlar si se asigna o no la variable command_stdout
. Debe pasar nil
en lugar de ""
cuando no haya datos. Es más claro cuando se entregan datos más adelante.
Probablemente me haya visto usando command + ';'
. La razón de esto se basa en la documentación de Kernel.exec (¿Qué es lo que utiliza popen3):
Si la cadena de la primera forma (exec ("orden")) sigue estas reglas simples :
- no hay meta caracteres
- ninguna palabra reservada cáscara y sin incorporado especial
- Rubí invoca el comando directamente sin cáscara
Puede forzar la invocación del shell agregando ";" a la cadena (porque ";" es un personaje meta)
Esto simplemente impide que un rubí de lanzar un error de 'spawn': No such file or directory
si pasa un comando incorrecto. En su lugar, pasará directamente al núcleo donde el error se resolverá correctamente y aparecerá como STDERR en lugar de una excepción no detectada.
Así que, digamos si expuse esta clase, completamente sin filtrar (no lo haré) a un usuario final, y proporcionaron un hash shadow y luego "nombre de usuario; rm -rf /" como nombre de usuario: esto no tiene el efecto de borrar '/' – arbales
Correcto. Los argumentos se pasan directamente al programa ejecutado. Puede verificar esto usted mismo. Intenta ejecutar el sistema 'ruby -e '* W (ls -l foo; rm -rf /)' ' – cam
ah, bueno genial. Tiene perfecto sentido. Creo que tengo esta idea de que garantizar una aplicación segura es, naturalmente, más difícil de lo que es, más difícil que simples pasos y hechos, como si existiera un caso extremo peligroso para todo.Esto es probable porque nunca he leído/aprendido mucho al respecto. – arbales