[solved] How to run a command and get its result, and how to intercept a shell context

Hello Crystal community,

I found different examples on the internet on how to run commands through Crystal, but they seem contradictory, and I would like to ask if you could provide one or more examples on how to run simple commands[1] and obtain their result (when there is a result).

Also, among the multiple commands I need to programmatically run, there is one command in particular which returns a new shell context, and I would like to know if Crystal could:

  • receive such shell context
  • run multiple command on this new context
  • exit from such context in some way, like running the “exit” command

If yes, I would like to know if you could provide some guidance or details about it.

Again, I cannot thank you enough for all the knowledge sharing on these years together!


[1] For example, with the Process class, or with another recommended class

Hello.
Sorry if I’m wrong. Are you looking for the following example?

require "process"

Process.run(command: "bash") do |s|
  i = s.input
  o = s.output
  i.puts "echo Hello"
  puts o.gets
  i.puts "echo World!"
  puts o.gets
  i.puts "exit"
end

Thanks a lot! I can’t believe it works. Crystal is amazing :slight_smile:

The only change I made is adding args, like:

require "process"

Process.run(command: "/usr/bin/ssh", args: ["12.34.56.78", "-p1234", "-i /home/user/.ssh/id_rsa"]) do |s|
  i = s.input
  o = s.output
  i.puts "hostname -f"
  puts o.gets
  i.puts "cat /etc/nginx/nginx.conf"
  puts o.gets
  i.puts "exit"
end

My only issue now isn’t related to Process (everything is perfect!), but with the “blocking” that occurs after reading commands which return multiple lines on s.output.

When I do:

require "process"

Process.run(command: "/usr/bin/ssh", args: ["12.34.56.78", "-p1234", "-i /home/user/.ssh/id_rsa"]) do |s|
  i = s.input
  o = s.output

  i.puts "cat /etc/nginx/nginx.conf"
  o.each_line do |line|
    puts line
  end

  i.puts "exit"
end

I’m able to read the complete output from cat (with its multiple lines), but Process.run remains stuck and doesn’t end, blocking the execution.

If the exit comes first, the solution is easy. Otherwise, I am not sure either.

require "process"

Process.run(command: "bash") do |s|
  i = s.input
  o = s.output
  Random.rand(10).times do |n|
    i.puts "echo Hello_#{n}"
  end
  i.puts "exit"
  puts o.gets_to_end
end

I don’t think it is a very good idea, but it seems to me that Timeout could be used.

require "process"

Process.run(command: "bash") do |s|
  i = s.input
  o = s.output
  o.read_timeout = 1
  Random.rand(10).times do |n|
    i.puts "echo Hello_#{n}"
  end
  begin
    while l = o.gets
      puts l
    end
  rescue IO::TimeoutError
    puts "Timeout #{o.read_timeout}"
  end
  i.puts "exit"
end

Thanks a lot @kojix2

In the end, there is a probability that this particular problem isn’t related to Crystal.

I will study it.