View mode: basic / threaded / horizontal-split · Log in · Help
March 13, 2005
I have a dream that...
..someday std.process would have a system call that would return the output of
a std.process.system() call.  Yes, I know I could pipe it a file and read the
file.  But perl has this beautiful thing,

@text = `somecmd opt1 opt2 etc`;

where the array @text now has all the output caused by the execution of

somecmd opt1 opt2 etc

there is no need to open a file or anything.  It's already there.  Oh, by the
way, I am not saying that it should be system, but something more like

char[][] systemOut()

instead of returning the always ambiguous int. :-)

Anyone agrees?  Or am I just in right field behind the first baseman?

thanks.

josé
March 13, 2005
Re: I have a dream that...
jicman wrote:
> I am not saying that it should be system, but something more like
> 
> char[][] systemOut()
> 
> instead of returning the always ambiguous int. :-)
> 
> Anyone agrees?  Or am I just in right field behind the first baseman?

Why char[][] and not char[]?  How would you get the exit value of the 
process without the int return?  What if you wanted to grab a stream 
other than stdout?  I think a more generic solution would be better, but 
then I personally don't really need to use this feature that much.
March 13, 2005
Re: I have a dream that...
I posted a popen implementation a bit earlier on.

news://news.digitalmars.com:119/cscp40$pee$1@digitaldaemon.com
http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/14470

I think this is like... close to what you want.  It allows you to read 
from the popen (Linux or Windows) as if it were a file.  Without the 
intermediate temporary file ;).

Any function that returned a string or array thereof would use something 
like this, likely, I would think.

-[Unknown]


> ..someday std.process would have a system call that would return the output of
> a std.process.system() call.  Yes, I know I could pipe it a file and read the
> file.  But perl has this beautiful thing,
> 
> @text = `somecmd opt1 opt2 etc`;
> 
> where the array @text now has all the output caused by the execution of
> 
> somecmd opt1 opt2 etc
> 
> there is no need to open a file or anything.  It's already there.  Oh, by the
> way, I am not saying that it should be system, but something more like
> 
> char[][] systemOut()
> 
> instead of returning the always ambiguous int. :-)
> 
> Anyone agrees?  Or am I just in right field behind the first baseman?
> 
> thanks.
> 
> josé
> 
>
March 13, 2005
Re: I have a dream that...
Niall FitzGibbon says...
>
>jicman wrote:
>> I am not saying that it should be system, but something more like
>> 
>> char[][] systemOut()
>> 
>> instead of returning the always ambiguous int. :-)
>> 
>> Anyone agrees?  Or am I just in right field behind the first baseman?
>
>Why char[][] and not char[]?

Hey, I take either.  The reason why char[][] is better is that I have everything
broken down in lines, for me.  There is no more

char[][] s = split(str,"\n\r");

But instead a beautiful foreach loop would give me all the lines. :-)

>How would you get the exit value of the 
>process without the int return?

That is the reason for the new add (systemOut) or whatever Walter wants.  I am
not picky.  If you want the int return value then use the actual system() call.
I very seldom care for the return, but more for the output.  It gives me
information, plus it tells me if it worked or not.  If I ever need to get the
int return, then I'd just use the now resident system function.

> What if you wanted to grab a stream other than stdout?

Good question, my friend.  Perhaps, systemErr() is in order. ;-)

>  I think a more generic solution would be better, but 
>then I personally don't really need to use this feature that much.

Then, sorry to say it, but why even ask those questions?  A nice yes or no would
suffice. ;-)  Just kidding man, but this is one of the reasons why this is
getting a little too much bandwidth.  If you didn't care, then why provoke with
the questions?  Sorry, but... ;-)

josé
March 13, 2005
Re: I have a dream that...
Unknown,

this would work.  I hadn't noticed this.  Still, though, a systemOut() also be
nice.

thanks.

Is there any way that you can zip this and attach it again?  I don't want to
copy and paste it and I am using the web browser and not the news reader.

thanks again.

jic

Unknown W. Brackets says...
>
>I posted a popen implementation a bit earlier on.
>
>news://news.digitalmars.com:119/cscp40$pee$1@digitaldaemon.com
>http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/14470
>
>I think this is like... close to what you want.  It allows you to read 
>from the popen (Linux or Windows) as if it were a file.  Without the 
>intermediate temporary file ;).
>
>Any function that returned a string or array thereof would use something 
>like this, likely, I would think.
>
>-[Unknown]
>
>
>> ..someday std.process would have a system call that would return the output of
>> a std.process.system() call.  Yes, I know I could pipe it a file and read the
>> file.  But perl has this beautiful thing,
>> 
>> @text = `somecmd opt1 opt2 etc`;
>> 
>> where the array @text now has all the output caused by the execution of
>> 
>> somecmd opt1 opt2 etc
>> 
>> there is no need to open a file or anything.  It's already there.  Oh, by the
>> way, I am not saying that it should be system, but something more like
>> 
>> char[][] systemOut()
>> 
>> instead of returning the always ambiguous int. :-)
>> 
>> Anyone agrees?  Or am I just in right field behind the first baseman?
>> 
>> thanks.
>> 
>> josé
>> 
>>
March 13, 2005
Re: I have a dream that...
jicman wrote:

> Then, sorry to say it, but why even ask those questions?  A nice yes or no would
> suffice. ;-)  Just kidding man, but this is one of the reasons why this is
> getting a little too much bandwidth.  If you didn't care, then why provoke with
> the questions?  Sorry, but... ;-)

Just interested, that's all.  Sorry if my post seemed confrontational or 
 unnecessary :)

I think such a feature would be good, but as I said I think it would be 
nicer to be able to retrieve the exit value *and* the stream output(s) 
with a single call -- in some situations there would be a need for both.

How about just an overload of the existing function?

int system(char[] command, out char[] stdOut, out char[] stdErr)
March 13, 2005
Re: I have a dream that...
Niall FitzGibbon says...
>
>jicman wrote:
>
>> Then, sorry to say it, but why even ask those questions?  A nice yes or no would
>> suffice. ;-)  Just kidding man, but this is one of the reasons why this is
>> getting a little too much bandwidth.  If you didn't care, then why provoke with
>> the questions?  Sorry, but... ;-)
>
>Just interested, that's all.  Sorry if my post seemed confrontational or 
>  unnecessary :)

It was not.  I was just kidding... If you haven't figured it by now, I'm a
kidder. :-)

>I think such a feature would be good, but as I said I think it would be 
>nicer to be able to retrieve the exit value *and* the stream output(s) 
>with a single call -- in some situations there would be a need for both.
>
>How about just an overload of the existing function?
>
>int system(char[] command, out char[] stdOut, out char[] stdErr)

This would be perfect, man.  Perfect indeed.  Now, if I could just convince
Walter and the rest of the gang, I'll be in good shape.

jic
March 13, 2005
Re: I have a dream that...
On Sun, 13 Mar 2005 21:49:35 +0000, Niall FitzGibbon wrote:

> jicman wrote:
> 
>> Then, sorry to say it, but why even ask those questions?  A nice yes or no would
>> suffice. ;-)  Just kidding man, but this is one of the reasons why this is
>> getting a little too much bandwidth.  If you didn't care, then why provoke with
>> the questions?  Sorry, but... ;-)
> 
> Just interested, that's all.  Sorry if my post seemed confrontational or 
>   unnecessary :)
> 
> I think such a feature would be good, but as I said I think it would be 
> nicer to be able to retrieve the exit value *and* the stream output(s) 
> with a single call -- in some situations there would be a need for both.
> 
> How about just an overload of the existing function?
> 
> int system(char[] command, out char[] stdOut, out char[] stdErr)

Now that would be a nice routine! 

It would simplify the process of capturing output to the point where it
would be a trivial exercise, and thus more people would bother to
incorporate it into their application design.

I know I could use it in Build ;-)

-- 
Derek Parnell
Melbourne, Australia
http://www.dsource.org/projects/build
14/03/2005 9:27:47 AM
March 13, 2005
Re: I have a dream that...
jicman wrote:
> Unknown,
> 
> this would work.  I hadn't noticed this.  Still, though, a systemOut() also be
> nice.
> 
> thanks.
> 
> Is there any way that you can zip this and attach it again?  I don't want to
> copy and paste it and I am using the web browser and not the news reader.

Here it is (watch out for word-wrap):

module popen;

private import std.c.stdio;
private import std.stream;

unittest
{
	// Just some old command; testing the stderr piping.
	ProcessStream test = new ProcessStream("diff 2>&1");

	std.stream.stdout.writeLine(test.readLine());
	std.stream.stdout.writeLine(test.readLine());
	std.stream.stdout.flush();

	// Talk about a useless waste :P!
	test = new ProcessStream("cat", FileMode.Out);

	test.writeLine("There's no place like stdout.");
}

// ProcessStream class - used to read or write (can't do both) from/to a 
process.
class ProcessStream: Stream
{
	// The FILE* handle to the process's output.
	private FILE* handle = null;
	// Seems this is private, so I had to override it; whether the file is 
open.
	private override bool isopen = false;

	// Create a new ProcessStream without any actual command/arguments.
	this()
	{
		super();

		// Clean up, this sets all the same variables anyway.
		this.close();
	}

	// A quick way to create the class and open it at the same time...
	this(char[] command, FileMode mode = FileMode.In)
	{
		super();

		this.open(command, mode);
	}

	~this()
	{
		this.close();
	}

	void open(char[] command, FileMode mode = FileMode.In)
	{
		// Make sure we're all closed off  ;) .
		this.close();

		// These are pretty simple and come nearly straight from the File class.
		this.readable = cast(bit) (mode & FileMode.In);
		this.writeable = cast(bit) (mode & FileMode.Out);
		this.seekable = false;

		// TODO: Binary flag?
		this.handle = popen(command, mode == FileMode.In ? "r" : "w");

		// Throw an OpenException if anything goes arye.
		if (this.handle == null)
		{
			this.isopen = false;
			throw new OpenException("couldn't pipe " ~ command);
		}
	}

	// Close the stream, or make sure everything is clean if the stream 
isn't open.
	override void close()
	{
		if (this.isopen && this.handle != null)
			pclose(this.handle);

		this.handle = null;
		this.isopen = false;
		this.readable = false;
		this.writeable = false;
		this.seekable = false;
	}

	override uint readBlock(void* buffer, uint size)
	in
	{
		// Not only must it be readable, but the handle must not be null 
(isopen might be equivalent.)
		assert(this.readable);
		assert(this.handle != null);
	}
	body
	{
		// Map straight to fread.
		return fread(buffer, 1, size, this.handle);
	}

	override uint writeBlock(void* buffer, uint size)
	in
	{
		// Check to make sure we're not writing to an unwritable stream.
		assert(this.writeable);
		assert(this.handle != null);
	}
	body
	{
		// Again, fwrite works perfectly for FILEs.
		return fwrite(buffer, 1, size, this.handle);
	}

	override ulong seek(long offset, SeekPos whence)
	{
		// If I throw an error here, nothing works, so zero will have to do.
		return 0;
	}
}

version (linux)
{
	// Blissfully easy.
	extern (C)
	{
		FILE* popen(char* command, char* type);
		int pclose(FILE* stream);
	}
}
else version (Windows)
{
	private import std.c.windows.windows;

	// For memset.
	private import std.string;

	// Some of this come from winbase.h.
	extern (Windows)
	{
		struct PROCESS_INFORMATION
		{
			HANDLE hProcess;
			HANDLE hThread;
			DWORD dwProcessId;
			DWORD dwThreadId;
		}
		struct STARTUPINFO
		{
			DWORD cb;
			LPTSTR lpReserved;
			LPTSTR lpDesktop;
			LPTSTR lpTitle;
			DWORD dwX;
			DWORD dwY;
			DWORD dwXSize;
			DWORD dwYSize;
			DWORD dwXCountChars;
			DWORD dwYCountChars;
			DWORD dwFillAttribute;
			DWORD dwFlags;
			WORD wShowWindow;
			WORD cbReserved2;
			LPBYTE lpReserved2;
			HANDLE hStdInput;
			HANDLE hStdOutput;
			HANDLE hStdError;
		}

		alias HANDLE* PHANDLE, LPHANDLE;
		alias STARTUPINFO* LPSTARTUPINFO;
		alias PROCESS_INFORMATION* LPPROCESS_INFORMATION;
		alias int intptr_t;

		const int O_BINARY = 0x8000, O_TEXT = 0x4000;
		const int O_RDONLY = 0, O_WRONLY = 1;
		const uint STD_INPUT_HANDLE = cast(uint) -10;
		const uint STD_OUTPUT_HANDLE = cast(uint) -11;
		const int STARTF_USESTDHANDLES = 0x00000100;
		const int DUPLICATE_CLOSE_SOURCE = 0x00000001;
		const int DUPLICATE_SAME_ACCESS = 0x00000002;
		const int NORMAL_PRIORITY_CLASS = 0x00000020;
		const int INFINITE = 0x0FFFFFFF;

		BOOL CloseHandle(HANDLE hObject);
		BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, 
LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
		BOOL CreateProcessA(LPCTSTR lpApplicationName, LPTSTR lpCommandLine, 
LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES 
lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID 
lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, 
LPPROCESS_INFORMATION lpProcessInformation);
		BOOL CreateProcessW(LPCTSTR lpApplicationName, LPTSTR lpCommandLine, 
LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES 
lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID 
lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, 
LPPROCESS_INFORMATION lpProcessInformation);
		BOOL DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE 
hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, 
DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
		BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode);
		HANDLE GetStdHandle(DWORD nStdHandle);
		DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
	}

	// Grab some useful C functions too.
	extern (C)
	{
		int _open_osfhandle(intptr_t osfhandle, int flags);
		FILE* fdopen(int fd, char *mode);
	}

	/* Okay, this is essentially a map from FILE pointer to HANDLE.
	   It's needed so the HANDLE can be freed at the same time the FILE
	   is, without changing the syntax of popen().  Alternatively, it
	   could be encapsulated within a class; but I like popen ^_^. */
	private HANDLE[FILE*] popenBuffer;

	/* This is the actual implementation of popen.  I put this together
	   from various examples on Google. */
	FILE* popen(char* command, char* type)
	{
		STARTUPINFO startup;
		PROCESS_INFORMATION process;
		SECURITY_ATTRIBUTES security;
		HANDLE inH, outH;

		// The handle needs to be inherited...
		security.nLength = SECURITY_ATTRIBUTES.sizeof;
		security.bInheritHandle = true;
		security.lpSecurityDescriptor = null;

		// Attempt to create the pipe between our two handles.
		if (!CreatePipe(&inH, &outH, &security, 2048L))
			return null;

		memset(&startup, 0, STARTUPINFO.sizeof);
		memset(&process, 0, PROCESS_INFORMATION.sizeof);

		startup.cb = STARTUPINFO.sizeof;
		startup.dwFlags = STARTF_USESTDHANDLES;
		// TODO: Check consistency on Linux (no stderr.)
		startup.hStdError = find(toString(command), "2>&1") != -1 ? outH : 
INVALID_HANDLE_VALUE;

		// A quick nested function to simplify the calls...
		void duplicateHandle(HANDLE fix)
		{
			HANDLE copy, self = GetCurrentProcess();

			if (!DuplicateHandle(self, fix, self, &copy, 0, FALSE, 
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
				fix = null;
			else
				fix = copy;
		}

		// We're going to use our handles - so, if we're reading we want to 
capture its stdout.
		if (type[0] == 'r')
		{
			duplicateHandle(inH);
			startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
			startup.hStdOutput = outH;
		}
		// Otherwise, we're writing, so we want to capture stdin.
		else
		{
			duplicateHandle(outH);
			startup.hStdInput = inH;
			startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
		}

		// TODO: Why won't CreateProcessW work here at all?
		if (!CreateProcessA(null, command, &security, &security, 
security.bInheritHandle, NORMAL_PRIORITY_CLASS, null, null, &startup, 
&process))
			return null;
		CloseHandle(process.hThread);

		// Now, we want to actually get the FILE (to emulate popen) so...
		int fno, mode = strlen(type) == 2 && type[1] == 'b' ? O_BINARY : O_TEXT;
		if (type[0] == 'r')
		{
			fno = _open_osfhandle(cast(long) inH, O_RDONLY | mode);
			CloseHandle(outH);
		}
		else
		{
			fno = _open_osfhandle(cast(long) outH, O_WRONLY | mode);
			CloseHandle(inH);
		}

		// Now it's child's play.  Open 'er up and stick the process handle in 
the lookup.
		// TODO: Why won't using plain type work?
		FILE* stream = fdopen(fno, "r+");
		popenBuffer[stream] = process.hProcess;

		return stream;
	}

	int pclose(FILE* stream)
	{
		DWORD termstat = 0;
		HANDLE process = popenBuffer[stream];

		// Flush any buffer(s) and say sianara.
		fflush(stream);
		fclose(stream);

		// Wait for it to quit and get its exit code... I hope INFINITE is 
okay here.
		WaitForSingleObject(process, INFINITE);
		GetExitCodeProcess(process, &termstat);
		CloseHandle(process);

		// Get rid of useless clutter explicitly.
		delete popenBuffer[stream];

		return termstat;
	}
}
else
{
	// This probably isn't needed; I'm sure most UNIX based operating 
systems have popen, no?  Does Mac OS X?
	static assert(0);
}

-- 
Justin (a/k/a jcc7)
http://jcc_7.tripod.com/d/
March 14, 2005
Re: I have a dream that...
That's easy, add this to the end of the command:

2&>1

If I remember correctly, that redirects stderr to stdout...

-[Unknown]


> What if you wanted to grab a stream other than stdout?
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home