Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?

Re: why IPC::Open3 can't execute MS-DOS "dir" command?

by afoken (Parson)
on Dec 05, 2009 at 13:50 UTC ( #811238=note: print w/ replies, xml ) Need Help??

in reply to why IPC::Open3 can't execute MS-DOS "dir" command?

Why do you take the pain to run an external program just to get a directory listing? And why do you add extra pain by using Microsofts bug-compatible command interpreter for that task? "If it hurts, stop doing it!"

Perl has opendir, readdir, closedir, stat, and the -X functions that easily allow re-implementing any directory lister in perl. Without the overhead of parsing output that was designed to please the human eye, not to be parsed easily. Without the overhead of creating an external process. Without the problems finding the right external helper program and make it return the expected result.

If you want to work with the files you wanted to get from running the dir builtin (no, it's not a standalone program, it's burried deep inside cmd.exe and another implementation with different behaviour is again burried deep inside, you better have a look at File::Find::Rule, File::Find, and perhaps some other CPAN modules like File::Spec and File::stat.

(Oh, and by the way: Unless you are still running Windows 9x or ME, there is no thing as "DOS" or even "MS-DOS". No NT-based Windows has an underlying DOS.)


Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Comment on Re: why IPC::Open3 can't execute MS-DOS "dir" command?
Re^2: why IPC::Open3 can't execute MS-DOS "dir" command?
by Anonymous Monk on Dec 07, 2009 at 01:49 UTC
    Thanks, Alexander. Actually I want to write an utility to run "DOS" commands on windows xp/vista, "dir" command is one of them. I would use other commands in the future, such as xcopy, ftp etc. Currently I'm truely going to output the files recursively in current folder.

      OK, then the first step is to learn about the different types of "DOS" commands. DOS and Windows have a long and ugly history, and you need to learn a little bit of that history if you want to do more than just pushing the mouse over the desktop. My ancient DOS manuals distinguished between three types of DOS commands:

      1. Internal commands, meaning commands built into
      2. External COM commands, i.e. commands compiled and linked into the COM format (no header, fixed start offset, one shared segment for code and data, limited to max. 64k-256 bytes)
      3. External EXE commands, i.e. commands compiled and linked into the EXE format (header, variable start offset, multiple segments for code and data, nearly unlimited size)

      One day, the DOS developers decided to look at the file data instead of the file extension when running external commands, so it was possible to rename EXE files to *.COM and vice versa. This allowed them to compile and link commands as EXE files while still using a COM file extension for backwards compatibility.

      So, you really need to distinguish between internal and external commands.

      With Windows 2000, XP, and newer, things become a little bit complicated, they inherited a very similar, but still different command shell named cmd.exe from the NT line, which behaves different and has different build-in commands. Of course, each Windows version has its own version of cmd.exe, all with different features. cmd.exe is the default shell on 2000 and newer, but is still there.

      dir, cls, echo and a few others are internal commands, i.e. you have to invoke them using or cmd.exe. Typically, you prefix the desired command with /c or cmd.exe /c, depending on the OS version and on which implementation of the command you want to execute.

      The majority of commands are external commands, most of then are EXE files, a few are renamed to *.COM for backwards compatibility. These commands can be executed directly, just like you would execute any other application (because that's what they are).

      The next DOS / Windows history lession is the command line. Unix shells expand, interpret and split the command line and pass an array of parameters to the invoked program. If you type foo *.txt *.asc into a Unix shell, the foo program is typically invoked with a list of all txt and asc files in the current directory. DOS and Windows shells do not expand or split the command line, instead, they pass it as a single string to the invoked program. If you type foo *.txt *.asc into a DOS or Windows shell, the foo program gets *.txt *.asc as a single command line string, and has to expand and split it in its very own way.

      Most DOS and Windows programs delegate that to the C runtime library (or equivalent), and so most DOS and Windows programs expand the command line in a very similar way. But there is no guarantee for that.

      Under DOS, the command line was limited to 126 bytes due to the COM file format, and sometimes, you still tun into this limit.

      The DOS / Windows shells still process the command line, e.g. they replace environment variables or remove redirections from the command line, so the command line is not passed completely unmodified to the invoked program. The rules to prevent this from happening (i.e. quoting) differ from shell to shell, and from OS version to OS version

      So invoking the DOS shell really begs for trouble. (Not that unix shells are paradise, their quoting rules are a little bit simplyer, but still differ from shell to shell.) Obviously, avoiding the shell as much as possible prevents problems. For exec and system, this means to use the multi-argument form. When you call exec or system with a single argument, perl may pass that argument to a random shell, leaving you with a lot of problems.

      On Windows, things become again problematic here, as there is no way to pass an array of parameters to the invoked program. Perl has to generate a single command line string from your arguments. And you have to hope that perls algorithm to construct the string is compatible with the invoked program's algorithm to split it again into an array.

      I/O redirection is once again problematic on Windows. On Unix, you fork, modify I/O and environment as needed in the forked copy of the current process, and then execute the real child, which inherits I/O redirection, environment and so on. On Windows, you have to do all this when creating a new process using the CreateProcess API function, and for more tricks, you need the CreateProcessEx API function. (I wonder when Microsoft will reach CreateProcessExExExEx ;-)) Don't worry, perl.exe will handle most of this for you. But it is not perfect.

      So, your really best bet on Windows is NOT to use external programs at all if you can avoid it.

      You do not need dir, as I've explained before. copy and xcopy can be replaced by File::Copy and File::Copy::Recursive. For ftp, you have a load of CPAN modules, like Net::FTP, LWP::Simple, LWP::UserAgent. cd won't work at all, but you can use chdir and Cwd.

      For many other commands, you will find superiour perl replacements, either build-in (e.g., the DOS find command is a crippled grep) or on CPAN. Only for very low-level access (fdisk, format, network config), you need either an external command or access to the Windows API. Often, Win32::API can be helpfull, sometimes DBD::WMI works, and if you are lucky, someone has already written a module and published it on CPAN, typically named Win32::Something.


      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://811238]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (9)
As of 2014-09-03 07:53 GMT
Find Nodes?
    Voting Booth?

    My favorite cookbook is:

    Results (35 votes), past polls