[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

1 Like

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
3 Likes

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
1 Like

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.

1 Like

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
1 Like

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.

1 Like