I have an unstoppable Ruby script that I need to be able to stop. Here is the following code:
require 'win32ole'
wsh = WIN32OLE.new("WScript.Shell")
def fileToArray(file)
x = []
File.foreach("#{file}") do |line|
x << line.to_s.split('')
end
return x.flatten!
end
tests = fileToArray("C:\\xampp\\htdocs\\x\\Script\\includes\\classes.php")
sleep 10
x = 0
y = tests.length
while x <= y do
send = tests[x]
speed = 0.025
if x == y
print "Test Complete"
break()
#You guys don't need to see this code, it's just detecting what keys are
#in the array and reading them to the file. But important to know that it is incrementing based on sent keys
else
x += 1
end
end
My problem is that the classes.php it is reading from is 4,000 lines long and takes a long time to get through. If it messes up, I have to wait until it is finished. There is no way for me to stop this loop from running until it is finished unless I completely log out from everything, CTRL+ALT+DEL Option: Logout. I've tried curses I've tried gets on exit. CTRL^C doesn't work either. I'd much rather have a written in solution, but otherwise I wouldn't mind knowing a few keys to killswitch the process a.k.a. "Kill it, kill it with keys"
You read each byte in an array, how long does your array get if you let in run till the end ? Filling an array of hundreds of thousands takes a long time indeed. Should see the rest of your code to decide if there is no better way, and why the win32ole object ?
If the standard windows keys to interrupt the jog don't word (Ctrl-c or Ctrl-break) and you can't use taskmanager, surely the console will still be responsive.
Here a script that does what you do. I put it in an endless loop so that I have to terminate it. The Process.pid shows the process id.
In advance you open a console and when you want to terminate the script you enter the following
taskkill /f /pid 5532
The /f is for forcing to terminate, the number is the pid you get from your script
# encoding:utf-8
STDOUT.sync = true
s1 = File.read __FILE__
puts Process.pid
class String
def to_a
while true # endless loop just for testing the kill
each_byte.inject([]){|result, char| result << char}
end
end
end
p s1.to_a
Tested with Windows 7 and Ruby MRI 1.9.3
EDIT:
based on your comment here another way to send keys to a program
If I understand correctly what you want to do you also don't need to split the string in advance.
require 'win32ole'
#for this script Autoit3 must be installed
s1 = "a string of thousands of characters"
# activate the correct window with the appclass
ai.Opt("WinTitleMatchMode", 4)
appClass = "[CLASS:xxxxxxxx]" # retrieved with AutoIt Window Info
ai = WIN32OLE.new("AutoItX3.Control")
ai.WinActivate(appClass)
# or with the handle
handle = "[HANDLE:#{ai.wingethandle(appClass)}]"
ai.WinActivate(handle)
# send keys, controlkeys or words to the program
ai.Send('{HOME}') #you can send control keys like that
s1.each_byte{|char| ai.Send(char); sleep 1} #to send everything char after char
s1[0..10].each_byte{|char| ai.Send(char); sleep 1} #to send the first 10 chars
Related
I have the following code snippet (a simplified representation of what I'm trying to do - training wheels). The sleep(2) would represent some network operation in my real code:
arr = []
5.times do |i|
rd, wr = IO.pipe
fork do
sleep(2) # I'm still waiting for the sleep to happen on each process ... not good, there is no parallelism here
rd.close
arr[i] = i
Marshal.dump(arr, wr)
end
wr.close
result = rd.read
arr = Marshal.load(result)
end
# Process.waitall
p arr
Q: is it possible to somehow create new processes in a loop, pass the results back but not waiting on each iteration. I'm pretty rusty and don't know / remember a great deal about IPC ... especially in Ruby.
Actual result is wait time of 2s*5 = 10s
Expected ~2s tootal (async processing of the sleep())
So a good comment clarifying things, explaining the theory would help a lot. Thanks.
In your loop you wait for each child process to write its results to the pipe before starting the next iteration.
The simplest fix would be to save the read ends of the pipes in an array and don’t read any of them until the loop is finished and you’ve started all the child processes:
arr = []
# array to store the IOs
pipes = []
5.times do |i|
rd, wr = IO.pipe
fork do
sleep(2)
rd.close
# Note only returning the value of i here, not the whole array
Marshal.dump(i, wr)
end
wr.close
#store the IO for later
pipes[i] = rd
end
# Now all child processes are started, we can read the results in turn
# Remember each child is returng i, not the whole array
pipes.each_with_index do |rd, i|
arr[i] = Marshal.load(rd.read)
end
A more complex solution if the wait/network times for different child processes variad might be to look at select, so you could read from whichever pipe was ready first.
Basically in my search for code which will loop, and terminate upon user input, i managed to find code here, and after some alteration, produced this:
#desired destination method, however loop persists!!
def desired_method
print "method entered"
end
Thread.new do
while line = STDIN.gets
break if line.chomp == "" # code detects user input
end
desired_method
end
# program will loop here until user presses enter
loop do
puts "foo"
sleep 1
end
This code is brilliant, and will enter the method 'desired_method' when i hit enter, however the loop persists!! printing 'foo' perpetually after "method entered"!!. I have done some research prior to posting this question on how to kill threads, which i believe may hold the answer. My attempts included naming the thread and using the 'thread.exit' function to kill it, however these techniques have remained unsuccessful.
Can anyone illustrate how i might enter the 'desired_method' method without the persisting "foo" print?
Thanks in advance, and greatly appreciated.
An easy solution here is to use semaphore, signalling between threads with a variable access to both places:
# This will be out stop flag, for signalling between threads.
#time_to_stop = false
def desired_method
print "method entered"
# Here we want the loop in the other thread to stop.
#time_to_stop = true
end
Thread.new do
while line = STDIN.gets
break if line.chomp == "" # code detects user input
end
desired_method
end
# program will loop here until user presses enter
loop do
puts "foo"
sleep 1
# only continue if the stop flag is not set.
break if #time_to_stop
end
Hope this helps.
I need a way to pause the program's flow because there are a lot of print statements that I want to check first. is there a way to do this with ruby, stop the program's flow and continue only if the user has entered yes or stop if it has entered no ? thanks
Yes. In your code, put gets. Then the code will pause at that point until the user inputs Enter. You don't need to do anything special to terminate because, if you want to, you can just do Ctrl+C.
Don't forget to chomp off the newline from the return value of gets.
n, m = 0, 1
repeat = 10
loop do
repeat.times do
print "#{m}, "
n, m = m, n + m
end
puts "\nContinue (yes/no)?"
answer = gets.chomp
exit if answer == "no"
end
Also check out Pry.
# test.rb
require 'pry'
class A
def hello() puts "hello world!" end
end
a = A.new
# start a REPL session
binding.pry
# program resumes here (after pry session)
puts "program resumes here."
I'm trying to find a way to terminate a loop when the user hits 'x'+Enter. I want the loop to just keep running in the background until the user cancels it.
Something along these lines:
while gets.chomp != 'x'
puts 'looping...'
sleep 1
end
I'm a beginner with programming and have searched far and wide for how to do this so any help would be deeply appreciated.
You have to use threads for this:
Thread.new do
while line = STDIN.gets
break if line.chomp == 'x'
end
exit
end
# whatever you want to do in the background
# (or rather in the foreground, actually)
loop do
puts "foo"
sleep 1
end
The problem is that STDIN.gets blocks, so you can't do something else at the same time without parallelizing the program by using a background thread that only checks for input.
I have a while loop consistently listening to incoming connections and outputting them to console. I would like to be able to issue commands via the console without affecting the output. I've tried:
Thread.new do
while true
input = gets.chomp
puts "So I herd u sed, \"#{input}\"."
#Commands would be in this scope
end
end
However, that seems to pause my entire script until input is received; and even then, some threads I have initiated before this one don't seem to execute. I've tried looking at TCPSocket's select() method to no avail.
Not sure where are the commands you want to "continue running" in your example. Try this small script:
Thread.new do
loop do
s = gets.chomp
puts "You entered #{s}"
exit if s == 'end'
end
end
i = 0
loop do
puts "And the script is still running (#{i})..."
i += 1
sleep 1
end
Reading from STDIN is done in a separate thread, while the main script continues to work.
Ruby uses green threads, so blocking system calls will block all threads anyway. An idea:
require 'io/wait'
while true
if $stdin.ready?
line = $stdin.readline.strip
p "line from stdin: #{line}"
end
p "really, I am working here"
sleep 0.1
end