Ruby's output in the Windows console VS mingw64 VS cygwin64 - ruby

I'm having a really weird problem here. Here's simple code that uses puts:
puts "Dale"
sleep 1
puts "Cooper"
I have 3 different terminals/consoles:
Windows console (system default)
mingw64 (UNIX-like terminal that was installed alongside with Git)
cygwin64 (UNIX-like terminal)
Here is weird thing: the code runs as expected only in the standard Windows console. UNIX-like terminals are waiting for 1 second and only then showing output (both lines at the same moment). Basically, UNIX-like terminals are waiting for the program to exit, and then they are showing the final result of output.
If I replace puts with print, it wont affect the execution process. UNIX-like terminals will still delay output until the program quits.
But the next two examples work right in all 3 terminals/consoles:
system("echo Dale")
sleep 1
system("echo Cooper")
This one adds quotes, but aside from this, the code works as expected.
p "Dale"
sleep 1
p "Cooper"
Having said this, I assume this has something to do with Ruby. I have tried different versions of Ruby.
Can someone explain why this is happening and what are possible ways to bypass this issue?

Here's me answering my own question.
Little background
If you do puts STDOUT.sync before the code then you will see that no matter if you are using Windows console or UNIX-like terminal, it will say that STDOUT.sync is set to false. That's weird thing because Windows console is flushing output immediately and UNIX-like terminals don't. I'm not sure why that's happening.
Solution
You can do STDOUT.flush (or $stdout.flush) to flush buffer or you can set STDOUT.sync (or $stdout.sync) to true. Both variants are completely friendly with Windows console. So the code will be as following:
puts "Dale"
STDOUT.flush
sleep 1
puts "Cooper"
or more recommended:
STDOUT.sync = true
puts "Dale"
sleep 1
puts "Cooper"
Determining whenever it's Windows console or UNIX-like terminal
Here is a little trick suggested by #eryksun to know if code is being run in Windows console or UNIX-like terminal. STDOUT.isatty works kind of inverted when run under Windows, but nevertheless it does the trick.
if STDOUT.isatty
# Windows console
else
# UNIX-like terminal
end
Keep in mind that this assumes that you already know that the code is being run under Windows. A good way to check OS is described here.
References
Major source for the answer can be found here. Idea for the answer belongs to #eryksun.
STDOUT.sync,
STDOUT.sync = (question about this method),
STDOUT.flush, STDOUT.isatty.

Related

how can I make a ruby program that sends to stdout in real time like copy does?

I don't have a great way to word this question's title. But I think I can explain what i'm asking for quite clearly.
The title of the question is, How can I make a ruby program that sends to stdout in real time like copy does?
I'll explain what I mean.
Suppose in one command prompt I do
C:\Windows\System32>md e:\exes
C:\Windows\System32>copy *.exe e:\exes >c:\carp\f.f
C:\Windows\System32>
Then from another command prompt I do
C:\carp>type f.f
I then see the f.f file build up, as the copy progresses.
and once the copy is complete, then f.f has the full stdout
However, this is not the case with my ruby program
Here is my ruby program
E:\rubylig>type putsandoutput.rb
20.times do
puts "a"
sleep 1
end
E:\rubylig>ruby putsandoutput.rb >a.a
If I then open another cmd prompt and do
E:\rubylog>type a.a
The a.a file is blank, until the putsandoutput.rb program has run to completion.
Then the a.a file is full.
But i'd like my ruby program to, like copy, be able to have its output redirected to the file, as it runs, rather than waiting till it completes.
Is it possible to do that. If so how, and if not then why not?
If not then i'm guessing it's a limitation of Ruby..
It's not a command line limitation, since 'copy' can do it.
You may be asking how to "flush" stdout. I thought that puts would flush automatically, so maybe it does not. Try $stdout.flush.

Difference in behaviour between Cygwin and Command Prompt when running Ruby scripts

I'm running exercise 14 of Learn Ruby the Hard Way. If I run the script in cmd it works fine, but I've been using Cygwin because it's nicer. When I run it in cygwin using this command:
ruby ex14.rb Devon
I get the following output
test
one
two
Hi Devon, I'm the ex14.rb script.
I'd like to ask you a few questions.
Do you like me Devon?
> Where do you live Devon?
> What kind of computer do you have?
> Alright, so you said test about liking me.
You live in one. Not sure where that is.
And you have a two computer. Nice.
That is to say, the program starts and immediately runs the three STDIN.gets.chomp() commands, and once it gets through those it puts and prints everything at once.
Is there a way to fix this behaviour? I would obviously want to have the lines run in the order they are written. I was unsure what to google for this type of error - combinations of "cygwin", "ruby", "puts output delayed" and "gets out of order" returned nothing relevant. Those search terms seem to vague anyway.
What exactly is going on, and is there a solution?
I think it is all to do with the CR LF differences between dos and unix.
try this...
set -o igncr
before running your script.

Ruby popen3 and ANSI colour

I am attempting to get watchr running tests automatically as files change, and got most of what I need working except for the fact that all ANSI colours from RSpec are being disregarded. The offending code is as follows:
stdin, stdout, stderr = Open3.popen3(cmd)
stdout.each_line do |line|
last_output = line
puts line
end
When cmd is equal to something like rspec spec/**/*.rb then the above code runs RSpec fine except that all output is in monochrome. I've looked at using Kernel.system instead, however system does not return the output which I need to determine if a test failed / succeeded. How can I get the output form a script that is executed from within Ruby including the ANSI color and output this to the console?
I would guess that rspec is examining the stream to which it is writing output to see if it is a tty (ie the console) or not, and if it's not, disabling colour. Many commands do this - GNU ls and grep, for example. Since the stream from the child process to your script is not a tty, colour will be disabled.
Hopefully, rspec has a flag which will force colour to be used, regardless of the stream type. If it doesn't, you will have to resort to some truly weird stty shenanigans.
Chances are good that the rspec tool is checking to see if it is running interactively on a terminal or being run automatically from a script before deciding to use color output or not.
The documentation says you can force color with --color command line option.
Try: rspec --color spec/**/*.rb.
It's possible to run commands in a pseudo terminal via the PTY module in order to preserve a user facing terminal-like behaviour. Credits go to the creator of the tty-command gem (see this issue) who implemented this behaviour in his gem:
require 'tty-command'
cmd = TTY::Command.new(pty: true)
cmd.run('rspec', 'spec/**/*.rb')
Keep in mind that using a pseudo terminal may have unwanted side effects, such as certain git commands using a pager which will essentially cause commands to hang. So introducing the functionality might be a breaking change.

"ruby script.rb" versus "xterm -e ruby script.rb"

I've run into a strange problem with Ruby that I can't explain. I have the following script that grabs whatever code is currently in the clipboard, runs it through a syntax highlighter, then puts the new version BACK into the clipboard:
#!/usr/bin/ruby1.9.1
require 'coderay'
language = "auto";
if(ARGV.length > 0)
language = ARGV[0];
end
print("Using language: #{language} \n");
codeToHighlight = `xsel --clipboard`
highlightedCode = CodeRay.scan(codeToHighlight, language.intern()).div
IO.popen("xsel --clipboard", mode='w') do |io|
io.write highlightedCode
io.flush
end
The odd part is that if I run it directly within a terminal, it works fine. If I run it via "xterm -e", however, it doesn't work. I found this thread on another site that asked the same question, but the person never got an answer: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/138423
That person found that if they added a pause at the end of the script like so...
10000.times do
puts ""
end
...it works. Why is this? Is there a way to fix this? I tried rewriting the script so that the popen returns an IO object and I could manually call close, but that doesn't make a difference.
How about if you execute it with gnome-terminal -e instead of xterm -e?
UPDATE:
OK, here is my best guess. You know how if you send a terminal program to the background (either with & after the command or with ctl-z) and then you close the terminal it kills the program, right? Well, xsel forks a child process to write to the clipboard, but it must be getting killed when the ruby wrapper script finishes and and xterm closes.
That would explain why the pause at the end allows it to work - it just gives enough time for the child process to finish before the terminal quits. It also explains why it works when run manually - you leave the terminal open long enough for the child process to finish.
Try adding the -n option to your xsel command, and I bet it works. -n keeps xsel from forking.

TCL hangs when trying to close TCL pipe

When launching tclsh and typing this:
close [open "|tclsh" w]
it works fine.
But, when in ~/.tclshrc you have package require Tk, the same line makes tclsh to HANG!
The same issue is with all GUI packages like Tk, Itk, Img, Iwidgets, however with not GUI packages like Itcl, it worsk fine.
How can I fix this issue? The point is to make tclsh not to hang, when typing close [open "|tclsh" w] with package require Tk in ~/.tclshrc.
The same issue is with wish. close [open "|wish" w] makes wish to HANG (with an empty ~/.wishrc file)!
I got this issue on both 32 and 64 bit CentOS.
I have the following versions of packages: tcl-8.5.8, tk-8.5.8, img-1.3, itcl-3.4.b1, itk-3.3, iwidgets-4.0.1.
Tcl applications mostly exit when they have finished their script, whether or not it is provided interactively. However the Tk package changes things around so that when the end of the script is reached, it instead goes into a loop handling events. If you're relying on an end-of-file causing things to exit, that's going to look a lot like a hang, but really it's just waiting properly for the GUI app to finish (so it can report the exit status of the subprocess).
The fix is to make a channel-readable event handler for stdin in the subprocess. There's a few ways to do this in detail, but here's a simple one that can go at the end of the bulk of code that you normally send:
proc ReadFromStdin {} {
if {[gets stdin line] >= 0} {
uplevel "#0" $line
} elseif {[eof stdin]} {
exit
} else {
# Partial read; try later when rest of data available
}
}
fileevent stdin readable ReadFromStdin
This assumes that each line is a full executable command; that might not be true, of course, but writing the code to use info complete to compose lines is less clear and possibly unnecessary here. (You know what you're actually sending better than I…)
My thought would be that it's waiting for wish to finish running, as per the man page:
If channelId is a blocking channel for
a command pipeline then close waits
for the child processes to complete.
Since wish enters an infinite loop (the event loop) and never exits, the close command will hang. Along the same lines, [package require Tk] (I believe) starts the event loop, so will cause the same behavior.
I'll admit though that it's loading .tclshrc at all, since
If there exists a file .tclshrc (or tclshrc.tcl on the Windows platforms) in the home directory of the user, interactive tclsh evaluates the file as a Tcl script just before reading the first command from standard input.
It seems odd to me that [open "|tclsh" w] winds up in an interactive shell.
As a side note, [pacakge require Tk] seems like a really strange thing to do in .tclshrc. In theory, you won't always want Tk (the window and event loop) when running Tcl (ie, command line only apps)... and, when you do want it, you know you do. To each their own, I suppose, it just seems odd to me.

Resources