8 Replies - 4888 Views - Last Post: 26 September 2013 - 12:09 PM

#1 baavgai  Icon User is online

  • Dreaming Coder
  • member icon

Reputation: 5777
  • View blog
  • Posts: 12,591
  • Joined: 16-October 07

"Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 05:01 AM

MOD EDIT:

This topic was split from the thread here:
http://www.dreaminco...o-processstart/

The discussion was good, but getting a bit off topic, so it can have its own thread.



This looked like fun, though I'm certain you can get all this data via WMI or ADSI.

First, the method:
string GetSystemInfo(string host = null, string username = null, string password = null) {
	var args = "/c systeminfo /fo csv";
	if (host != null) { args += " /s " + host; }
	if (username != null) { args += " /u " + username; }
	if (password != null) { args += " /s " + password; }
	var pr = new Process();
	pr.StartInfo.FileName = "cmd.exe";
	pr.StartInfo.Arguments = args;
	pr.StartInfo.UseShellExecute = false;
	pr.StartInfo.RedirectStandardOutput = true;
	pr.Start();
	string result = pr.StandardOutput.ReadToEnd();
	pr.WaitForExit();
	return result;
}



Note the /c, your /k buggered me up something fierce.

A bonus: For more fun, we can run this puppy asynchronously on a whole list:
static List<KeyValuePair<string, string>> GetSystemInfos(string[] hosts, string username = null, string password = null) {
	Func<object, string> action = (obj) => { return GetSystemInfo((string)obj, username, password); };
	Task<string>[] tasks = new Task<string>[hosts.Length];
	for (int i = 0; i < tasks.Length; i++) {
		tasks[i] = Task<string>.Factory.StartNew(action, hosts[i]);
	}
	Task.WaitAll(tasks);

	var results = new List<KeyValuePair<string, string>>();
	for (int i = 0; i < tasks.Length; i++) {
		results.Add(new KeyValuePair<string, string>(hosts[i], tasks[i].Result));
	}
	return results;
}


This post has been edited by Curtis Rutland: 26 September 2013 - 12:48 PM


Is This A Good Question/Topic? 0
  • +

Replies To: "Argument not being passed to Process.Start" Discussion

#2 Curtis Rutland  Icon User is online

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4429
  • View blog
  • Posts: 7,698
  • Joined: 08-June 10

Re: "Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 06:42 AM

Here's another way to run that in parallel:

static Dictionary<string, string> GetInfos(IEnumerable<string> hosts, string username = null, string password = null)
{
    return hosts.AsParallel().ToDictionary(h => h, h => GetSystemInfo(h, username, password));
}


Was This Post Helpful? 2
  • +
  • -

#3 baavgai  Icon User is online

  • Dreaming Coder
  • member icon

Reputation: 5777
  • View blog
  • Posts: 12,591
  • Joined: 16-October 07

Re: "Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 07:46 AM

Neat. And, I like the IEnumerable (why didn't I do that?). It's worth noting that AsParallel doesn't behave in exactly the same way as Tasks. I find the Task thingy generally works better, though the LINQ thingy is a lot nicer to look at.

With LINQ in mind, here's a Task 2.0 version:
List<KeyValuePair<string, string>> GetSystemInfos(IEnumerable<string> hosts, string username = null, string password = null) {
	Func<object, KeyValuePair<string, string>> action = (obj) => { return new KeyValuePair<string, string>((string)obj, GetSystemInfo((string)obj, username, password)); };
	var tasks = hosts.Select((host) => { return Task<KeyValuePair<string, string>>.Factory.StartNew(action, host); }).ToArray();
	Task.WaitAll(tasks);
	return tasks.Select((x) => { return x.Result; }).ToList();
}


Was This Post Helpful? 1
  • +
  • -

#4 Curtis Rutland  Icon User is online

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4429
  • View blog
  • Posts: 7,698
  • Joined: 08-June 10

Re: "Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 08:12 AM

Nice. I personally prefer to use a Dictionary when dealing with key-value pairs. I know that it's technically different in the way it stores data, but I find it to be convenient and expressive.

Here's how I would do it with tasks:

var items = hosts.Select(h => new { Key = h, Task = Task.Factory.StartNew(() => GetSystemInfo(h)) });
Task.WaitAll(items.Select(i => i.Task).ToArray());
return items.ToDictionary(i => i.Key, i => i.Task.Result);


Was This Post Helpful? 1
  • +
  • -

#5 baavgai  Icon User is online

  • Dreaming Coder
  • member icon

Reputation: 5777
  • View blog
  • Posts: 12,591
  • Joined: 16-October 07

Re: "Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 08:30 AM

Hmm... I like that one better than mine. Damn, I hate when that happens. :P
Was This Post Helpful? 0
  • +
  • -

#6 Curtis Rutland  Icon User is online

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4429
  • View blog
  • Posts: 7,698
  • Joined: 08-June 10

Re: "Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 08:36 AM

:D

But I really do practice. I try to make my code as expressive as possible, so I have to comment as little as possible. When your code tells you what it does, comments are redundant. I've found that once you've learned how to read LINQ and Lambdas, loops are actually harder to read, because the logic of what's going on is broken up across several lines.
Was This Post Helpful? 0
  • +
  • -

#7 baavgai  Icon User is online

  • Dreaming Coder
  • member icon

Reputation: 5777
  • View blog
  • Posts: 12,591
  • Joined: 16-October 07

Re: "Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 09:27 AM

Been working in F# for the past few months. Loops? What these loop things. I come back to C# to play with loops.
Was This Post Helpful? 0
  • +
  • -

#8 Curtis Rutland  Icon User is online

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4429
  • View blog
  • Posts: 7,698
  • Joined: 08-June 10

Re: "Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 09:47 AM

Have you found a good use case for it? I've always wanted to do a few projects, but every time I start, I realize that nobody else at work would be able to support it. What have you found its best at?
Was This Post Helpful? 0
  • +
  • -

#9 baavgai  Icon User is online

  • Dreaming Coder
  • member icon

Reputation: 5777
  • View blog
  • Posts: 12,591
  • Joined: 16-October 07

Re: "Argument not being passed to Process.Start" Discussion

Posted 26 September 2013 - 12:09 PM

Well, of course, it's general purpose, so you could use it for anything. However, in shines in playing with collections data. My best use case is data interfaces. Pull data from a pair of sources and sync up the changes.

My F# style database reader is a sequence which does all the cleanup:
let dataSeq (parser:int->obj->'a) (cmd:DbCommand) =
  let parseRow (dr:DbDataReader) =
    [| 0..dr.FieldCount-1 |] 
    |> Array.map (fun i->parser i (dr.GetValue(i)))
  seq {
    try
      cmd.Connection.Open()
      use dr = cmd.ExecuteReader()
      while dr.Read() do 
        yield parseRow dr
    finally
      cmd.Connection.Close()
    }




Some standard interface code looks like:
let getData (side:DI.StreamSide): DI.KeyData list =
  let cmd = function
    | DI.SideSrc->
      @"SELECT * FROM VwIfaceHR"
      |> DB.getCmd srcConn
    | DI.SideDst->
      cleanData()
      let dstFieldsStr = String.concat ", " (DI.FieldList.names fields)
      sprintf "SELECT %s FROM %s" dstFieldsStr dstTable
      |> DB.getCmd dstConn
  cmd side
  |> DB.dataSeq (DI.FieldList.parser fields side)
  |> List.ofSeq 
  |> List.map (fun a->Array.get a 0, a)



The heart of the interface engine is simply:
let runner debug (logger:Logger) (si:IStandardInterface) = 
  let loadData side =
    let recs = si.GetData side
    LogCount (side, recs.Length) |> logger
    recs

  let update = si.Update logger

  let run update =
    if si.RunBefore logger then
      compareData (loadData SideSrc) (loadData SideDst)
      |> List.iter update
      si.RunAfter logger

  match debug with
  | true -> run update
  | false -> 
    let safeUpdate row = 
      try
        update row
      with
        | ex -> LogError (sprintf "ROW ERROR: %s " (ex.Message)) |> logger
    try
      run safeUpdate
    with
      | ex -> LogError (sprintf "ERROR: %s " (ex.Message)) |> logger
  LogEnd |> logger




Ok, that was way off topic, but there it is.

Somewhat apropos to the thread, I've found F# fsi far easier to deal with than PowerShell. You get the same scripting goodness as PowerShell, much nicer language, and VS for development.

So, the C# code in question in F#:
let runProcess prog args =
  let pr = new System.Diagnostics.Process()
  pr.StartInfo.FileName <- prog
  pr.StartInfo.Arguments <- args
  pr.StartInfo.UseShellExecute <- false
  pr.StartInfo.RedirectStandardOutput <- true
  pr.Start() |> ignore
  let result = pr.StandardOutput.ReadToEnd()
  pr.WaitForExit();
  result

let getSysInfo user pass host =
  let add k v = match v with | None->"" | Some x->sprintf " %s %s" k x
  sprintf "/c systeminfo /fo csv %s %s %s" (add "/u" user) (add "/p" pass) (add "/s" host)
  |> runProcess "cmd.exe" 

let getSysInfos user pass (hosts: string list) =
  let cmd x = getSysInfo user pass (Some x)
  let tasks = hosts |> List.map (fun host->host, Task.Factory.StartNew(fun () -> cmd host))
  Task.WaitAll(tasks |> List.map snd |> Seq.cast<Task> |> Array.ofSeq)
  tasks |> List.map (fun (k,t)->(k,t.Result))



Doubtless there are more F#y ways to do that, but I wanted to duplicate the functionality on the table.
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1