Discussion:
Simulating keystrokes for automated user input
Ryan Mohr
2010-06-15 21:45:22 UTC
Permalink
I've written a simple console app (type command. get results. repeat.)
and I'm looking for a way to send it simulated keystrokes (hoping to
remotely send commands through a socket connection and have them "typed"
in the main console). I thought the solution would be easy but I'm
finding it much harder than anticipated.

The solution needs to generate a keystroke programmatically such that
the console has no idea whether it was physically typed by the user or
not.

Tried writing to a modified stdin, directly editing /dev/tty, redirected
echos... no luck yet.

Any ideas?
--
Posted via http://www.ruby-forum.com/.
Joel VanderWerf
2010-06-15 22:39:04 UTC
Permalink
Post by Ryan Mohr
I've written a simple console app (type command. get results. repeat.)
and I'm looking for a way to send it simulated keystrokes (hoping to
remotely send commands through a socket connection and have them "typed"
in the main console). I thought the solution would be easy but I'm
finding it much harder than anticipated.
The solution needs to generate a keystroke programmatically such that
the console has no idea whether it was physically typed by the user or
not.
Tried writing to a modified stdin, directly editing /dev/tty, redirected
echos... no luck yet.
What about ruby's PTY lib?

PTY.spawn 'your-app' do |r,w,cid| ... end
b***@gmail.com
2010-06-15 23:00:29 UTC
Permalink
This post might be inappropriate. Click to display it.
Brian Candler
2010-06-16 08:18:20 UTC
Permalink
Post by Ryan Mohr
The solution needs to generate a keystroke programmatically such that
the console has no idea whether it was physically typed by the user or
not.
Tried writing to a modified stdin, directly editing /dev/tty, redirected
echos... no luck yet.
Any ideas?
Spawn a pty. There's pty.so in the standard library, which is a C
extension and unfortunately not well documented, but google for
"PTY.spawn" to find some examples, e.g.

http://www.ruby-forum.com/topic/133560

http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/ext/pty/expect_sample.rb?view=markup
--
Posted via http://www.ruby-forum.com/.
Ryan Mohr
2010-06-16 20:02:23 UTC
Permalink
Post by Brian Candler
Spawn a pty. There's pty.so in the standard library, which is a C
extension and unfortunately not well documented, but google for
"PTY.spawn" to find some examples, e.g.
Thanks for the suggestion Brian but I don't think that will work for my
case. I need to send keystrokes to stdin of the current process itself.
Maybe there's a way to do that with PTY but I couldn't get it to work.

Here's a quick example that demonstrates the issue:

[code]
# prompt is already up and waiting
t = Thread.new {
print "Enter command: "
$stdout.flush

# get the next command to run from the user
command = gets

puts "Executing command #{command}"
}

# want this to enter the desired command as though it
# was typed by the user himself (show up in stdout but
# also be picked up by any open 'gets' requests)

# ... ? (send "foo" to stdin of the current process)


t.join

puts "Done."
[/code]
--
Posted via http://www.ruby-forum.com/.
Brian Candler
2010-06-16 20:15:03 UTC
Permalink
Post by Ryan Mohr
Post by Brian Candler
Spawn a pty. There's pty.so in the standard library, which is a C
extension and unfortunately not well documented, but google for
"PTY.spawn" to find some examples, e.g.
Thanks for the suggestion Brian but I don't think that will work for my
case. I need to send keystrokes to stdin of the current process itself.
Then I think you're stuck. When your ruby process was started, its stdin
file descriptor was connected to something (e.g. a terminal or a pipe).
Only that thing can generate data for your app to read; e.g. you can't
write to the read end of a pipe.

You could connect forcibly reopen stdin on a different fd:

rd, wr = IO.pipe
STDIN.reopen(rd)
Thread.new do
wr.puts "hello"
end
line = gets
puts line.inspect

That's really horrible though. It would be better to fork your program
entirely:

IO.popen("-","rb+") do |pipe|
if pipe
# parent
pipe.puts "hello"
res = pipe.gets
puts "got: #{res}"
else
# child
line = gets
puts line.inspect
end
end

If what you're trying to accomplish is unit testing of a CLI-driven
program, then I'd suggest either running the program under test as a
child (e.g. using IO.popen or PTY.spawn), or factor out your objects so
that they can read and write to arbitrary IO objects that you pass in,
so that you can pass them a StringIO or one end of a pipe.

Otherwise, perhaps you can explain what you are trying to achieve?

Regards,

Brian.
--
Posted via http://www.ruby-forum.com/.
Ryan Mohr
2010-06-16 22:34:43 UTC
Permalink
This post might be inappropriate. Click to display it.
b***@gmail.com
2010-06-16 22:53:18 UTC
Permalink
Post by Brian Candler
Otherwise, perhaps you can explain what you are trying to achieve?
In addition to the CLI, I'm developing a simple gui interface.  As the
user interacts with the gui, commands are sent back to the CLI through a
socket connection.
At this point my CLI has nearly 100 commands and growing, so the easiest
approach would be for the GUI to just send the text command desired and
have it "typed" into the main app.
In the GUI program run the CLI via PTY.spawn and let the GUI program
write ("type") commands (directly) to the CLI (instead of using a
socket). If the GUI and CLI must run on separate hosts, launch the
CLI remotely via, for example, Net::SSH. As a third alternative write
a small helper program to run on the server, it will run the actual
CLI via PTY.spawn and listen on the socket for connections from the
GUI.
Jesse Jurman
2010-06-16 23:45:17 UTC
Permalink
I don't exactly understand what your trying to make, but in any
instance, whether it's gui or not, you might have a program turn your
input into a file, and then have your original program check if such a
file exists, read it, and then delete it... I haven't tried something
like it yet, but I plan on using a technique like it in one of my own
programs.
--
Posted via http://www.ruby-forum.com/.
Continue reading on narkive:
Loading...