Rishodi Posted September 22, 2010 Share Posted September 22, 2010 (edited) I've written a couple of wrapper functions as follows to simplify interacting with the Windows command line interpreter. The first one executes commands, and the second one reads everything that was output to stdout.Func RunCmd($sCmd, $sDir = Default, $showFlag = Default, $optFlag = Default) If ($sCmd == Default) Or ($sCmd == "") Then Return SetError(1, 0, 0) If ($sDir == Default) Then $sDir = "" If ($optFlag = Default) Then $optFlag = $STDERR_MERGED Local $cmdPID = Run(@COMSPEC & " /c " & $sCmd, $sDir, $showFlag, $optFlag) Return SetError(@error, @extended, $cmdPID) EndFunc Func StdoutReadAll($iPID) Local $output = "" Do $output &= StdoutRead($iPID) Until $error StdioClose($iPID) Return $output EndFuncThe command I'm currently trying to use is "fsutil fsinfo drives" which reports a list of the active drive letters. I've since realized that there's a built-in function for doing just that (DriveGetDrive), but I want to know how to resolve my issue in case it pops up again when I'm using some command for which there isn't an existing function in AutoIt. Using the functions above, all I need to do is make a couple calls like so:Local $cmdPID = RunCmd("fsutil fsinfo drives", @SystemDir) Local $result = StdoutReadAll($cmdPID) MsgBox(0, "Result", $result)The problem is that $result only includes the output from stdout up through the first drive. The remaining drives are not listed. Curious, I decided to read data from stdout in binary format instead, and I found that null characters separate the drive letters instead of spaces.Expected output is this in hex: 0x4472697665733A20433A5C20443A5C20573A5C20Which in string format is this: Drives: C:\ D:\ W:\Actual output is this in hex: 0x4472697665733A20433A5C00443A5C00573A5C00Which in string format is this: Drives: C:\I've removed the CRLFs here (0x0D0A) to highlight the problem, which is that null characters (0x00) are where the spaces (0x20) should be. AutoIt apparently lops off the rest of the string after encountering the first null character. How would I dodge this issue? If I read from stdout in binary instead of ASCII, is there a way to manipulate the data by replacing all occurrences of 0x00 with 0x20 before converting to a String? Is there (shouldn't there be) an easier way? Edited September 22, 2010 by Rishodi Link to comment Share on other sites More sharing options...
enaiman Posted September 22, 2010 Share Posted September 22, 2010 Well, not the best solution at all but you might try to delay the exit from your Do/Until loop. Func StdoutReadAll($iPID) Local $output = "", $STD_read = "", $S_Counter = 0 While 1 Sleep(10) $STD_read = StdoutRead($iPID) If $STD_read = "" Then $S_Counter += 1 Else $output &=$STD_read EndIf If $S_Counter > 5 Then ExitLoop WEnd StdioClose($iPID) Return $output EndFunc I don't particulary like it because it doesn't rely on @error but it might work in your case. What you could do is make a second function (StdoutReadAll_2) to use only in this case. SNMP_UDF ... for SNMPv1 and v2c so far, GetBulk and a new example script wannabe "Unbeatable" Tic-Tac-Toe Paper-Scissor-Rock ... try to beat it anyway :) Link to comment Share on other sites More sharing options...
maqleod Posted September 22, 2010 Share Posted September 22, 2010 (edited) StdoutRead does not block, it will return immediately. In order to get all data, it must be called in a loop. Peeking on the stream does not remove the data from the buffer, however, it does return the available data as normal. By default, data is returned in text format. By using the binary option, the data will be returned in binary format. That is from the help file, it gives a sample like so: #include <Constants.au3> Local $foo = Run(@ComSpec & " /c dir foo.bar", @SystemDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD) Local $line While 1 $line = StdoutRead($foo) If @error Then ExitLoop MsgBox(0, "STDOUT read:", $line) Wend Edited September 22, 2010 by maqleod [u]You can download my projects at:[/u] Pulsar Software Link to comment Share on other sites More sharing options...
PsaltyDS Posted September 23, 2010 Share Posted September 23, 2010 (edited) Strings are terminated by a null, but not binary data. Don't forget there is a binary parameter for StdReadOut() Edit: Where did "$error" come from in this: Until $error Shouldn't that be: Until @error I don't see your binary null in the output when I test this, by the way: #include <Constants.au3> Global $sExtCmd = "fsutil.exe fsinfo drives" Global $iPID = Run($sExtCmd, @SystemDir, @SW_MINIMIZE, $STDOUT_CHILD) Global $sOut = "" Do $sOut &= StdoutRead($iPID) Until @error ConsoleWrite("$sOut = " & $sOut & @LF) MsgBox(64, "Result", "$sOut = " & $sOut) Edited September 23, 2010 by PsaltyDS Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law Link to comment Share on other sites More sharing options...
AndyG Posted September 23, 2010 Share Posted September 23, 2010 does $result=stringreplace(StdoutReadAll($cmdPID),chr(0),chr(0x20))work? Link to comment Share on other sites More sharing options...
Rishodi Posted September 23, 2010 Author Share Posted September 23, 2010 enaiman: Thanks, but unfortunately, that won't help me. I've tested my script to see how the data comes in from stdout in this particular case, and it's always the same every time. The first iteration of the loop finds nothing, the second iteration returns all data, and on the third iteration @error is non-zero and the loop exits. maqleod: I'm aware of that, as I've read everything from the help file I could find that was relevant. Notice that the function I wrote calls StdoutRead in a loop; the only difference is that I'm using a Do-Until and the example in the help file uses a While loop. PsaltyDS: Yes, I am aware that there's a binary parameter for StdoutRead(). Otherwise, I couldn't have discovered the fact that the CLI is printing null characters to stdout after each drive letter. And yes, $error should be @error in the script in the OP. ($error was just left over from some troubleshooting I was doing.) I ran your test script, and there's still a null character after the first drive (the C:\ drive), so the rest of the drive listing is lost when the data is read as a string. What version of Windows are you running? I'm on XP SP3; I could certainly appreciate it if newer releases changed the behavior so that null characters were not printed in the middle of a command's output. AndyG: I didn't expect your suggestion to work, but it did! I had assumed that calling StdoutRead() with binary=false was truncating the string at the first null character, but that's not the case. The string is not being truncated until it's displayed (console, msgbox, etc.), so the fix is as simple as calling StringReplace. Thanks! Link to comment Share on other sites More sharing options...
PsaltyDS Posted September 23, 2010 Share Posted September 23, 2010 Sounds like you have your solution, but my demo didn't show the symptoms (no nulls) on Vista Business 32-bit. I did look at the binary value and the nulls just weren't there. Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law Link to comment Share on other sites More sharing options...
Rishodi Posted September 23, 2010 Author Share Posted September 23, 2010 Sounds like you have your solution, but my demo didn't show the symptoms (no nulls) on Vista Business 32-bit. I did look at the binary value and the nulls just weren't there.I believe you, which is why I asked which OS you're using. Perhaps the output of the command was adjusted in Vista?In any case, I think we can all agree that null characters shouldn't appear in the middle of a command's output stream like this. Link to comment Share on other sites More sharing options...
trancexx Posted September 23, 2010 Share Posted September 23, 2010 There's something strange about you - says one string to another.Wtf are you talking aboutru&*xW2/^%fcya/r("os?#?*n8#9Mx>q - says the other.All jokes aside, running one program to run another program that runs another program... geez ♡♡♡ . eMyvnE Link to comment Share on other sites More sharing options...
AdamUL Posted September 23, 2010 Share Posted September 23, 2010 Try this. Func StdoutReadAll($iPID) Local $output = "" ProcessWaitClose($iPID) $output = StdoutRead($iPID) StdioClose($iPID) Return $output EndFunc This is how I usually read output without having to use a loop and haven't had any problems with it. Adam Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now