RunCMD() v0.94 : Capture stdout to variable. Non-blocking version. Pre-process/omit individual lines.

Post your working scripts, libraries and tools
User avatar
SKAN
Posts: 853
Joined: 29 Sep 2013, 16:58

RunCMD() v0.94 : Capture stdout to variable. Non-blocking version. Pre-process/omit individual lines.

14 Apr 2020, 13:00

RunCMD() is a rewrite and renamed version of my old StdOutToVar() which was adapted from Sean's StdoutToVar

RunCMD( CmdLine, WorkingDir, Codepage, Fn )
RunCMD() runs a console utility in windowless mode and captures its output one line at a time, concatenates them and returns the complete text.
The lines that are read maybe redirected to a helper function for pre-process. Within the helper functions individual lines they maybe omitted or reformatted.
Unlike previous versions, the current RunCMD() v0.93 reads from a non-blocking pipe enabled via PIPE_NOWAIT flag passed to SetNamedPipeHandleState()
During the lifetime of the created process, a global array element A_Args.RunCMD.PID will contain the PID and assigning a 0 (false) to this element will cancel RunCMD().
The created process will inherit the Priority of the script. Therefore Process, Priority,, High maybe used in scripts to see some marginal improvement.
 
Parameters:
  • CmdLine : The command line to be executed.
  • WorkingDir : Use this parameter to specify Working Directory. If omitted the launched process will default to A_WorkingDir.
  • Codepage : Default is Cp0. Use UTF-8 or UTF-16 when dealing with unicode.
  • Fn : Helper function name. When a helper function is available, RunCMD() will call it with two parameters Line, LineNum.
    You may control the output within the helper function.
 
  
Examples
 
Powershell examples: https://www.autohotkey.com/boards/viewtopic.php?p=341237#p341237
 
 
The function:

Code: Select all

RunCMD(CmdLine, WorkingDir:="", Codepage:="CP0", Fn:="RunCMD_Output") {  ;         RunCMD v0.94        
Local         ; RunCMD v0.94 by SKAN on D34E/D37C @ autohotkey.com/boards/viewtopic.php?t=74647                                                             
Global A_Args ; Based on StdOutToVar.ahk by Sean @ autohotkey.com/board/topic/15455-stdouttovar

  Fn := IsFunc(Fn) ? Func(Fn) : 0
, DllCall("CreatePipe", "PtrP",hPipeR:=0, "PtrP",hPipeW:=0, "Ptr",0, "Int",0)
, DllCall("SetHandleInformation", "Ptr",hPipeW, "Int",1, "Int",1)
, DllCall("SetNamedPipeHandleState","Ptr",hPipeR, "UIntP",PIPE_NOWAIT:=1, "Ptr",0, "Ptr",0)

, P8 := (A_PtrSize=8)
, VarSetCapacity(SI, P8 ? 104 : 68, 0)                          ; STARTUPINFO structure      
, NumPut(P8 ? 104 : 68, SI)                                     ; size of STARTUPINFO
, NumPut(STARTF_USESTDHANDLES:=0x100, SI, P8 ? 60 : 44,"UInt")  ; dwFlags
, NumPut(hPipeW, SI, P8 ? 88 : 60)                              ; hStdOutput
, NumPut(hPipeW, SI, P8 ? 96 : 64)                              ; hStdError
, VarSetCapacity(PI, P8 ? 24 : 16)                              ; PROCESS_INFORMATION structure

  If not DllCall("CreateProcess", "Ptr",0, "Str",CmdLine, "Ptr",0, "Int",0, "Int",True
                ,"Int",0x08000000 | DllCall("GetPriorityClass", "Ptr",-1, "UInt"), "Int",0
                ,"Ptr",WorkingDir ? &WorkingDir : 0, "Ptr",&SI, "Ptr",&PI)  
     Return Format("{1:}", "", ErrorLevel := -1
                   ,DllCall("CloseHandle", "Ptr",hPipeW), DllCall("CloseHandle", "Ptr",hPipeR))

  DllCall("CloseHandle", "Ptr",hPipeW)
, A_Args.RunCMD := { "PID": NumGet(PI, P8? 16 : 8, "UInt") }      
, File := FileOpen(hPipeR, "h", Codepage)

, LineNum := 1,  sOutput := ""
  While (A_Args.RunCMD.PID + DllCall("Sleep", "Int",0))
    and DllCall("PeekNamedPipe", "Ptr",hPipeR, "Ptr",0, "Int",0, "Ptr",0, "Ptr",0, "Ptr",0)
        While A_Args.RunCMD.PID and (Line := File.ReadLine())
          sOutput .= Fn ? Fn.Call(Line, LineNum++) : Line

  A_Args.RunCMD.PID := 0
, hProcess := NumGet(PI, 0)
, hThread  := NumGet(PI, A_PtrSize)

, DllCall("GetExitCodeProcess", "Ptr",hProcess, "PtrP",ExitCode:=0)
, DllCall("CloseHandle", "Ptr",hProcess)
, DllCall("CloseHandle", "Ptr",hThread)
, DllCall("CloseHandle", "Ptr",hPipeR)

, ErrorLevel := ExitCode

Return sOutput  
}
HotKeyIt
Posts: 2158
Joined: 29 Sep 2013, 18:35
Contact:

Re: RunCMD()

14 Apr 2020, 15:36

Nice :thumbup: and v2 compatible :D
burque505
Posts: 1398
Joined: 22 Jan 2017, 19:37

Re: RunCMD()

14 Apr 2020, 17:14

@SKAN, thank you. Works great! I've been using a version of your 2013 script modded by @maz-1, found here for some time now, I'll try to replace that with your new script where appropriate.
Also, thanks to @HotKeyIt and @Sean for their contributions to this process.

Regards,
burque505
User avatar
SKAN
Posts: 853
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

16 Apr 2020, 16:36

@HotKeyIt Thank you :thumbup: :)
@burque505 Thanks for the feedback! :)

With Process Priority high I'm seeing about 7% (max) increase with my c compiler.
To confirm that the setting is really applied, , I ran notepad with RunCMD() and checked in "Windows Task Manager".
It works.. :)
User avatar
Cerberus
Posts: 169
Joined: 12 Jan 2016, 15:46

Re: RunCMD()

18 Apr 2020, 13:38

Thank you very much for this, it works!

I'm having one little issue, though, which may not be relevant to your function. When I use it to error-check an Autohotkey script (see code), I don't get the proper output for certain Unicode characters from the error messages:

Code: Select all

ScriptFile = C:\Users\User\Desktop\test.ahk
Command := A_AhkPath . " /iLib NUL /ErrorStdOut """ ScriptFile """"
Msgbox % "RunCMD gives this:`n`n" RunCmd(Command)
When the file test.ahk contains just €Msgbox, the output of the function I get is this:
Image

It works as expected, except that the sign becomes â?¬. Lexikos told me a while ago that I should be looking into StdOutToVar() and CreateProcess, which I couldn't figure out at the time; so I was hoping it might be possible to get the right output using your new function. Or is it not possible?
User avatar
boiler
Posts: 6597
Joined: 21 Dec 2014, 02:44

Re: RunCMD()

18 Apr 2020, 15:50

In case you wanted a logo for this... :D
run cmd.jpg
run cmd.jpg (39.62 KiB) Viewed 3754 times
burque505
Posts: 1398
Joined: 22 Jan 2017, 19:37

Re: RunCMD()

18 Apr 2020, 16:18

:bravo:
User avatar
SKAN
Posts: 853
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

19 Apr 2020, 05:17

@Cerberus

I tried your example I can reproduce the effect. (The scriptfile was saved as utf-8)
I don't have any suggestions right now :(
User avatar
Cerberus
Posts: 169
Joined: 12 Jan 2016, 15:46

Re: RunCMD()

19 Apr 2020, 07:46

@SKAN Thank you for trying. Then at least I know I have done what I could.
burque505
Posts: 1398
Joined: 22 Jan 2017, 19:37

Re: RunCMD()

19 Apr 2020, 08:55

Deleted code per @SKAN request.

Code: Select all

msgbox % RunCmd("cmd /c echo €", A_ScriptDir, "CP65001")
For me, this gives:
Euro.PNG
Euro.PNG (6.51 KiB) Viewed 3601 times
EDIT: Same output with this:

Code: Select all

msgbox % RunCmd("cmd /c echo €", A_ScriptDir, "UTF-8")
Last edited by burque505 on 19 Apr 2020, 10:01, edited 5 times in total.
User avatar
SKAN
Posts: 853
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

19 Apr 2020, 09:11

Dear @burque505
You wrote:

Code: Select all

RunCmd(CmdLine, WorkingDir:="", Cp:="CP65001") { ; Originally "CP0"
Please don't alter the codepage default value of the function. It should be "CP0".
@Lexikos has mentioned elsewhere n times that the default value should be "CP0".
Also, the purpose of renaming StdOutToVar() to RunCMD() is that the former exists in too many variants.
Please edit out the function from your example, and just post the example as follows.

Code: Select all

msgbox % RunCmd("cmd /c echo €", A_ScriptDir, "CP65001")
using CP65001 shows me a ? instead of euro symbol.

Thanks, :)
User avatar
Cerberus
Posts: 169
Joined: 12 Jan 2016, 15:46

Re: RunCMD()

19 Apr 2020, 16:44

@burque505 Hi! Thank you for thinking along with me there. The thing is, I'm looking for a way to get error messages from Autohotkey into a variable. I'm making a linter, which uses A_AhkPath . " /iLib NUL /ErrorStdOut """ ScriptFile """ 2>&1 |more" or similar to get any load errors that Autohotkey.exe perceives in a script; the problem is that the command don't properly output Unicode in the text from the error messages. Currently, I use ComObjCreate("WScript.Shell") and .Exec to do it, which has the same problem with Unicode.
burque505
Posts: 1398
Joined: 22 Jan 2017, 19:37

Re: RunCMD()

19 Apr 2020, 17:06

Hi @Cerberus. So I take it this didn't/doesn't work?

Code: Select all

#Include RunCmd.ahk
msgbox % RunCmd(A_AhkPath . " /iLib NUL /ErrorStdOut """ ScriptFile """ 2>&1 |more", A_ScriptDir, "UTF-8")
EDIT: I can see after testing a file of my own it does NOT work. :facepalm:

Best of luck, by the way! I'm looking forward to seeing the linter when you get finished with it.
Regards,
burque505
User avatar
Cerberus
Posts: 169
Joined: 12 Jan 2016, 15:46

Re: RunCMD()

20 Apr 2020, 19:06

@burque505
Thank you for testing again. Lexikos suggested that StdoutToVar() might produce the correct Unicode output, because it uses CreateProcess; I never got around to testing that, because I couldn't figure out how to get StdoutToVar() working. Now, in SKAN's code, I noticed DllCall("CreateProcess", ... ), so I was hoping this might solve the issue, but alas.

The linter is mostly finished. There are a few minor issues left to solve, but nothing that hinders the use the the linter or could cause any problems. So far, it catches any load error, and a few warnings from rules in modules I have made myself (others can make additional modules by dropping in files). The errors and warnings can be viewed on the command line, or processed by a plugin in an editor, to provide live error marking. Such plug-ins should be easy to make; I have made one for Sublime Text, which works well.
User avatar
SKAN
Posts: 853
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

21 Apr 2020, 02:31

@Cerberus

Try this :)

Code: Select all

MsgBox % RunCmd( A_ComSpec . " /U /C echo €",,"UTF-16")

CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF] [[/S] [/C | /K] string]

/C Carries out the command specified by string and then terminates
/A Causes the output of internal commands to a pipe or file to be ANSI
/U Causes the output of internal commands to a pipe or file to be Unicode


burque505 wrote: Deleted code per @SKAN request.
Thanks friend! :)
User avatar
Cerberus
Posts: 169
Joined: 12 Jan 2016, 15:46

Re: RunCMD()

21 Apr 2020, 17:56

@SKAN Thank you for your help. The thing is, I'm trying to catch error message output by Autohotkey.exe: I'm not just trying to echo something. So I really need to use something like this, for the command that is to be executed:

Code: Select all

ScriptFile = C:\Users\User\Desktop\test.ahk
Command := A_AhkPath . " /iLib NUL /ErrorStdOut """ ScriptFile """ " ;2>&1 |more"
Msgbox % "RunCMD gives this:`n`n" RunCmd(Command,,"UTF-16")
Using UTF-16 gives me lots of Chinese characters. The file is saved as UTF-8, but using that as a parameter still garbles the € sign.

Lexikos suggested that the issue could be solved by using something like 'CreateProcess' as it was used in StdOutToVar():
lexikos wrote:
Cerberus wrote:
08 Apr 2019, 15:10
Is that a limitation of Windows exec [...]?
Yes. The workaround is to use CreateProcess directly. Search "StdoutToVar" for examples.
https://www.autohotkey.com/boards/viewtopic.php?p=272412#p272412
But perhaps that is not the case after all.
HotKeyIt
Posts: 2158
Joined: 29 Sep 2013, 18:35
Contact:

Re: RunCMD()

21 Apr 2020, 20:03

AFAIK, in ahk source code for unicode build setlocale() needs to be added to ERR_PRINT -> #define ERR_PRINT(fmt, ...) _ftprintf(stderr, fmt, __VA_ARGS__, setlocale(LC_ALL, ".65001")).
Just tested the fix and seems to work fine.
User avatar
SKAN
Posts: 853
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

21 Apr 2020, 20:12

HotKeyIt wrote:
21 Apr 2020, 20:03
AFAIK, in ahk source code for unicode build setlocale() needs to be added to ERR_PRINT -> #define ERR_PRINT(fmt, ...) _ftprintf(stderr, fmt, __VA_ARGS__, setlocale(LC_ALL, ".65001")).
Just tested the fix and seems to work fine.
Awesome! Many thanks. :)
User avatar
Cerberus
Posts: 169
Joined: 12 Jan 2016, 15:46

Re: RunCMD()

21 Apr 2020, 20:16

@HotKeyIt
Ohh this is great! Thanks for looking and testing. I really have no idea how Github works, but do you think there is any chance that this could be added to Autohotkey eventually?
User avatar
SKAN
Posts: 853
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

21 Apr 2020, 20:23

Cerberus wrote:
21 Apr 2020, 17:56
The thing is, I'm trying to catch error message output by Autohotkey.exe: I'm not just trying to echo something. So I really need to use something like this, for the command that is to be executed:
I was approaching the problem in steps. The echo was to demonstrate that Dir command can output unicode filenames correctly
when /U switch is used.

The second step I just did is as follows:
1) Copy test.ahk to autohotkey folder
2) Open Run dialog type CMD /U and drop to command prompt
3) Navigate to AutoHotkey folder and executed the following
Autohotkey.exe /ErrorStdOut test.ahk 2> try.txt

and here is the output of try.txt
D:\AutoHotkey\test.ahk (1) : ==> This line does not contain a recognized action.
Specifically: ?Msgbox


I was about to tell you that I had doubts that AutoHotkey was not outputting UTF-8
Luckily @HotKeyIt clarified it.

Return to “Scripts and Functions”

Who is online

Users browsing this forum: halweg, robodesign, TheArkive, TheProdigyC2 and 17 guests