[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Damn you cmd.exe!

Adam Skobi

10/16/2008 10:39:00 PM

[Note: parts of this message were removed to make it a legal post.]

Hi,

As an exercise in metaprogramming I decided to write a DSL shell. One of the
requirements was that it should be working on both - win32 and unix
platforms. I quickly encountered some problems with cmd.exe.

My first understanding was that I open a stream to cmd.exe and write/read it
synchronously. In linux it would look like this

IO.popen( "/bin/bash", "r+" ) do |cmd|
cmd.puts 'pwd'
puts cmd.gets
end

Everything's dandy. It works like a charm. When I try to create an
equivalent in win32:

IO.popen( "cmd.exe", "r+" ) do |cmd|
cmd.puts 'cd'
puts cmd.gets
end

This thing fails miserably. I can't really pinpoint what is the root cause
of it. I have found here on comp.lang.ruby two solutions. First one:

IO.popen( "cmd.exe", "r+" ) do |cmd|
Thread.new(cmd) do |io|
while line = io.gets
puts line
end
end

cmd.puts "dir"
cmd.puts "cd \\"
cmd.puts "dir"

sleep 3
end

It works but is sucks badly on many levels as you can see. Other one:

IO.popen("cmd.exe /c #{single_command}") do | pipe |
pipe.each_line { |line| puts line }
end

This thing is not so bad but there is a small problem. I want my DSL user to
be able to write such simple commands as

cd C:dir

The problem is although the both command will run correctly, the change of
current directory will be lost between the commands. I can think of some
crazy workarounds but I was wondering if there is a way for the cmd.exe to
behave normally. Any help would be appreciated.

--
Adam Skobi

6 Answers

Mike Gold

10/17/2008 2:11:00 AM

0


cmd.exe would seem like a red herring here. Subprocesses do not modify
the environment of their parents, whether it's a cmd.exe process or
whether you've executed 'cd' directly without the shell.

The case is the same on Linux and Windows. Even if Windows had a cd.exe
executable (you could use cygwin's cd), it wouldn't help you.

Your purpose is unclear to me. If you want to make your own shell, you
read the "cd" string and translate it to Dir.chdir. If you want to run
a persistent cmd.exe, then your program is the one-liner
exec("cmd.exe"). Do you somehow seek something in between?
--
Posted via http://www.ruby-....

Roger Pack

10/17/2008 6:14:00 AM

0

> As an exercise in metaprogramming I decided to write a DSL shell. One of
> the
> requirements was that it should be working on both - win32 and unix
> platforms. I quickly encountered some problems with cmd.exe.

Note that EventMachine has a popen that might be useful.
--
Posted via http://www.ruby-....

Adam Skobi

10/17/2008 7:27:00 AM

0

[Note: parts of this message were removed to make it a legal post.]

Sorry for not beeing clear enough.
I want to have a consistent way of putting commands into the shell of both
platforms - win32 and linux. The linux solution that I posted seems natural
- open the /bin/bash and than operate by puts and gets. I was wondering if
it's possible to do the exact same thing with the cmd.exe i.e. without
opening the cmd.exe for each command (cmd.exe /C #{one_command_at_a_time})

The exec("cmd.exe") is not a solution as I want to have control of the input
and output.

--
Adam Skobi

Robert Klemme

10/17/2008 8:37:00 AM

0

On 17.10.2008 00:39, Adam Skobi wrote:
> As an exercise in metaprogramming I decided to write a DSL shell. One of the
> requirements was that it should be working on both - win32 and unix
> platforms. I quickly encountered some problems with cmd.exe.
>
> My first understanding was that I open a stream to cmd.exe and write/read it
> synchronously.

You probably rather want to read asynchronously though because
synchronously does not work properly under all conditions (namely if
more data is sent back and forth than the operating system's pipe buffer
size can handle, typically 4k AFAIK). You might rather want to try this:

require 'open3'

Open3.open3 "your command" do |stdin, stdout, stderr|
threads = [
Thread.new { stdout.each {|line| puts line} },
Thread.new { stderr.each {|line| puts line} },
]

begin
...

stdin.puts "exit"
ensure
threads.each {|th| th.join}
end
end

> Everything's dandy. It works like a charm. When I try to create an
> equivalent in win32:

Do you use the native Windows Ruby or cygwin?

> IO.popen( "cmd.exe", "r+" ) do |cmd|
> cmd.puts 'cd'
> puts cmd.gets
> end
>
> This thing fails miserably. I can't really pinpoint what is the root cause
> of it.

Then what does "fails miserably" mean? Any error messages?

> I have found here on comp.lang.ruby two solutions. First one:
>
> IO.popen( "cmd.exe", "r+" ) do |cmd|
> Thread.new(cmd) do |io|
> while line = io.gets
> puts line
> end
> end

Yep, that's better anyway (see above).

> cmd.puts "dir"
> cmd.puts "cd \\"
> cmd.puts "dir"
>
> sleep 3
> end
>
> It works but is sucks badly on many levels as you can see. Other one:

You need to remember the thread and join it at the end of the block if
you want to make sure that all output from the shell is printed before
you leave the block.

> IO.popen("cmd.exe /c #{single_command}") do | pipe |
> pipe.each_line { |line| puts line }
> end
>
> This thing is not so bad but there is a small problem. I want my DSL user to
> be able to write such simple commands as
>
> cd C:> dir
>
> The problem is although the both command will run correctly, the change of
> current directory will be lost between the commands. I can think of some
> crazy workarounds but I was wondering if there is a way for the cmd.exe to
> behave normally. Any help would be appreciated.

Well, what is "normal" anyway? Not all operating systems are similar
and just because Window's shell behaves differently does not make it
abnormal. :-)

Kind regards

robert

Adam Skobi

10/17/2008 9:29:00 AM

0

[Note: parts of this message were removed to make it a legal post.]

On Fri, Oct 17, 2008 at 10:39 AM, Robert Klemme
<shortcutter@googlemail.com>wrote:

>
>
> Everything's dandy. It works like a charm. When I try to create an
>> equivalent in win32:
>>
>
> Do you use the native Windows Ruby or cygwin?
>

Native Windows Ruby.


>
> IO.popen( "cmd.exe", "r+" ) do |cmd|
>> cmd.puts 'cd'
>> puts cmd.gets
>> end
>>
>> This thing fails miserably. I can't really pinpoint what is the root cause
>> of it.
>>
>
> Then what does "fails miserably" mean? Any error messages?
>

Yes i do. It writes in my native language but it translates to something
along the lines: "The process tried to write to a non-existent stream"


>
>
> I have found here on comp.lang.ruby two solutions. First one:
>>
>> IO.popen( "cmd.exe", "r+" ) do |cmd|
>> Thread.new(cmd) do |io|
>> while line = io.gets
>> puts line
>> end
>> end
>>
>
> Yep, that's better anyway (see above).
>
> cmd.puts "dir"
>> cmd.puts "cd \\"
>> cmd.puts "dir"
>>
>> sleep 3
>> end
>>
>> It works but is sucks badly on many levels as you can see. Other one:
>>
>
> You need to remember the thread and join it at the end of the block if you
> want to make sure that all output from the shell is printed before you leave
> the block.
>


>
> IO.popen("cmd.exe /c #{single_command}") do | pipe |
>> pipe.each_line { |line| puts line }
>> end
>>
>> This thing is not so bad but there is a small problem. I want my DSL user
>> to
>> be able to write such simple commands as
>>
>> cd C:>> dir
>>
>> The problem is although the both command will run correctly, the change of
>> current directory will be lost between the commands. I can think of some
>> crazy workarounds but I was wondering if there is a way for the cmd.exe to
>> behave normally. Any help would be appreciated.
>>
>
> Well, what is "normal" anyway? Not all operating systems are similar and
> just because Window's shell behaves differently does not make it abnormal.
> :-)
>


You see, speaking simply, I want to have control on what 'goes in' and what
'goes out'. I don't want to have a background thread reading everything from
cmd.exe as I wont be able to tell which command produced the output. I
haven't tested it thoroughly but I think it should look like in /bin/bash.
Simple stuff:

shell.puts 'cd /home/me'
shell.puts 'pwd'
puts shell.gets # /home/me

I am really bedazzled as to why it doesn't seem to work in win32.

--
Adam Skobi

Robert Klemme

10/17/2008 9:50:00 AM

0

On 17.10.2008 11:29, Adam Skobi wrote:
> On Fri, Oct 17, 2008 at 10:39 AM, Robert Klemme

> Native Windows Ruby.

There are some quirkynesses with that implementation which I'm not
familiar with. I use cygwin and it has served me well.

>> IO.popen( "cmd.exe", "r+" ) do |cmd|
>>> cmd.puts 'cd'
>>> puts cmd.gets
>>> end
>>>
>>> This thing fails miserably. I can't really pinpoint what is the root cause
>>> of it.
>>>
>> Then what does "fails miserably" mean? Any error messages?
>
> Yes i do. It writes in my native language but it translates to something
> along the lines: "The process tried to write to a non-existent stream"

This works from cygwin:

irb(main):012:0> IO.popen "cmd.exe", "r+" do |io|
irb(main):013:1* t = Thread.new {io.each {|line| p line}}
irb(main):014:1> io.puts "dir /w", "exit"
irb(main):015:1> t.join
irb(main):016:1> end
"Microsoft Windows XP [Version 5.1.2600]\r\n"
"(C) Copyright 1985-2001 Microsoft Corp.\r\n"
"\r\n"
"C:\\cygwin\\home\\Robert>dir /w\n"
" Volume in Laufwerk C: hat keine Bezeichnung.\r\n"
" Volumeseriennummer: 5C71-9E29\r\n"
"\r\n"
" Verzeichnis von C:\\cygwin\\home\\Robert\r\n"
"\r\n"
"[.] [..] .bashrc .bash_aliases
..bash_history\r\n"
".bash_profile .inputrc .irbrc [bin] x.xml\r\n"
" 7 Datei(en) 10.364 Bytes\r\n"
" 3 Verzeichnis(se), 57.749.262.336 Bytes frei\r\n"
"\r\n"
"C:\\cygwin\\home\\Robert>exit\n"
=> #<Thread:0x7ffa6fc0 dead>
irb(main):017:0>

>> Well, what is "normal" anyway? Not all operating systems are similar and
>> just because Window's shell behaves differently does not make it abnormal.
>> :-)
>
> You see, speaking simply, I want to have control on what 'goes in' and what
> 'goes out'. I don't want to have a background thread reading everything from
> cmd.exe as I wont be able to tell which command produced the output. I
> haven't tested it thoroughly but I think it should look like in /bin/bash.
> Simple stuff:
>
> shell.puts 'cd /home/me'
> shell.puts 'pwd'
> puts shell.gets # /home/me
>
> I am really bedazzled as to why it doesn't seem to work in win32.

This solution is flawed as I have tried to explain earlier. Note that
you will create deadlocks if you do not read the output stream properly
(i.e. asynchronously).

If you want to make sure you know which command produced which output
you need to nevertheless read in the background and parse output for the
prompt. Then you know when sub process output has finished and can
attribute the output to commands properly. You also need proper
synchronization for this as well.

Note that you can also use File#expect.

Insisting that the world should be different won't help because that has
little effect on cmd.exe's implementation. You'll have to work with
what you got.

robert