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

Post your working scripts, libraries and tools for AHK v1.1 and older
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: RunCMD() - Code updated

15 May 2020, 15:49

SKAN wrote:
13 May 2020, 15:53
rommmcek wrote:GetKeyState("Pause", "P")
Thanks. :)
Works well.. but I feel uncomfortable to use it. I've updated the code to use the global A_Args array instead.
A_Args.RunCMD.PID will now contain the PID of the console process.
I'm not sure whether it will create a problem in a race condition.


@hasantr
Function updated.
You may call A_Args.RunCMD.PID := 0 from a hotkey or routine to exit gracefully... or
call Process, Close, % A_Args.RunCMD.PID to terminate.


If anyone needs a long running process to test the updated function, try this: MsgBox % RunCmd(A_Comspec . " /c Dir *.* /s", "C:")
Thanks. This works pretty well. I have tested. Maybe the only problem is that all processes are terminated.

Code: Select all

df := RunCmd("ping -n 10 192.168.1.35")
MsgBox % df
return

w::
df1 := RunCmd("ping 192.168.1.34")
MsgBox % df1
return

q::
A_Args.RunCMD.PID := 0
Process, Close, % A_Args.RunCMD.PID
return

For example, I can run two commands. If we just want to end one?

Also.
I don't know if it's possible, but is it okay to get text before finishing? If I wanted to check the cmd output to see if everything was fine but I did not interfere with the study.

Did I want a lot? :)
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: RunCMD() - Code updated

15 May 2020, 16:21

hasantr wrote:

Code: Select all

q::
A_Args.RunCMD.PID := 0
Process, Close, % A_Args.RunCMD.PID
return
What exactly do you expect to happen when you do that? Effectively you are doing a Process, Close, 0
I suggested you use one of those line.. not both together.
For example, I can run two commands. If we just want to end one?
AutoHotkey is a single threaded application. It cannot run two loops simultaneously.
You should not call RunCmd() twice.. it has a loop.
I don't know if it's possible, but is it okay to get text before finishing?
How do you want to "get" the text?
One way is that RunCmd() can call an another function within which you can monitor the progress.
Did I want a lot?
I don't think so. Just be sure on what you want.
My Scripts and Functions: V1  V2
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: RunCMD() - Code updated

18 May 2020, 03:16

SKAN wrote:
15 May 2020, 16:21
hasantr wrote:

Code: Select all

q::
A_Args.RunCMD.PID := 0
Process, Close, % A_Args.RunCMD.PID
return
What exactly do you expect to happen when you do that? Effectively you are doing a Process, Close, 0
I suggested you use one of those line.. not both together.
For example, I can run two commands. If we just want to end one?
AutoHotkey is a single threaded application. It cannot run two loops simultaneously.
You should not call RunCmd() twice.. it has a loop.
I don't know if it's possible, but is it okay to get text before finishing?
How do you want to "get" the text?
One way is that RunCmd() can call an another function within which you can monitor the progress.
Did I want a lot?
I don't think so. Just be sure on what you want.
Thanks SKAN. Now I have reshaped my work.

Actually, what I need is to ping the devices. But if the device is not available, there is a long timeout.
Ping the device, if no response in 0.2 seconds, return with stdout.

So I want to send runcmd by timing.
Is it a good way to use a timer for this? If I use Timer, I will have created two workpieces, but there will be no loop.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: RunCMD() - Code updated

18 May 2020, 04:16

hasantr wrote:
18 May 2020, 03:16
Actually, what I need is to ping the devices. But if the device is not available, there is a long timeout.
Ping the device, if no response in 0.2 seconds, return with stdout.
The DllCall("ReadFile",...) called in loop is a synchronous command.. means, until A_Index=2, the process or the loop cannot be interrupted.
For example if you RunCmd("notepad.exe"), the loop will never see A_Index=2 until you close notepad.
Therefore, whether you can close a Ping within 0.2 seconds depends on whether Ping lets DllCall("ReadFile",...) reach it 2nd interation within 0.2 seconds.
Try tuning the ms values in following code.

Code: Select all

#NoEnv
#SingleInstance, Force

SetTimer, RunCmd_Cancel, -200                   ; 200 ms
df := RunCmd("ping -w 200 -n 10 192.168.1.35")  ; -w 200 ms
Msgbox % df
Return

RunCmd_Cancel:
  A_Args.RunCMD.PID := 0
Return
My Scripts and Functions: V1  V2
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: RunCMD()

18 May 2020, 04:16

As you said, this will do the job:

Code: Select all

SetTimer, TimeOutRunCMD, -230 ; take in account launching time too
RunCmd(...)
Return

TimeOutRunCMD:
    A_Args.RunCMD.PID := 0
Return
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: RunCMD()

18 May 2020, 04:58

@SKAN @rommmcek Teşekkür ederim.

Thank you for explaining the logic of work.
Setting ping time as a parameter is not suitable for Android devices. If the device does not answer, the ADB makes a long wait in any case.
 But I can now do this by looking at your examples. Thanks again.

Should I cancel the timer if the process rotates in less than 200 ms?

Edit:
Using this option sometimes freezes.
A_Args.RunCMD.PID := 0

This works well.
Process, Close, % A_Args.RunCMD.PID

What is the difference between them?
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: RunCMD()

18 May 2020, 09:30

Ha, ha you got two almost identical answers at the same time.
SKAN already predicted your problem. Read it once again and try it with "notepad.exe"!
A_Args.RunCMD.PID := 0 makes the while loop to exit at next repetition (which obviously sometimes last to start longer that you would like)
Process, Close, % A_Args.RunCMD.PID kills the process launched by the function, like you would do it manually in the Task Manager, not waiting for anything.
If you use the last approach then it is desirable to switch off the timer after RunCMD(…), because there is a slim chance (very very slim though) that a new process would be launched having the same PID (very unlikely) as the one launched by RunCMD() and already finished, what would then terminate that process in the remainder of 200ms time gap (very unlikely again). But to be 100% sure switch the timer off!
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: RunCMD()

18 May 2020, 14:00

rommmcek wrote:
18 May 2020, 09:30
Ha, ha you got two almost identical answers at the same time.
SKAN already predicted your problem. Read it once again and try it with "notepad.exe"!
A_Args.RunCMD.PID := 0 makes the while loop to exit at next repetition (which obviously sometimes last to start longer that you would like)
Process, Close, % A_Args.RunCMD.PID kills the process launched by the function, like you would do it manually in the Task Manager, not waiting for anything.
If you use the last approach then it is desirable to switch off the timer after RunCMD(…), because there is a slim chance (very very slim though) that a new process would be launched having the same PID (very unlikely) as the one launched by RunCMD() and already finished, what would then terminate that process in the remainder of 200ms time gap (very unlikely again). But to be 100% sure switch the timer off!
Even two answers were not enough, I'm still learning slowly. :)
Thank you. Yes I understood when I read it again. I have a hard time understanding English.
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: RunCMD()

02 Jun 2020, 02:33

Hello there. This code runs on the cmd screen but does not result with RunCMD Where am I doing wrong.

Code: Select all

dir /S /B /A:-D *.mp3 *.mp4 *.jpg | findstr /V /I /C:""\\Microsoft\\"" /C:""\\Windows\\""
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

02 Jun 2020, 06:51

hasantr wrote:
02 Jun 2020, 02:33
Hello there. This code runs on the cmd screen but does not result with RunCMD Where am I doing wrong.

Code: Select all

dir /S /B /A:-D *.mp3 *.mp4 *.jpg | findstr /V /I /C:""\\Microsoft\\"" /C:""\\Windows\\""
Change the WorkingDir as per your need:

Code: Select all

RunCmd( A_Comspec . " /c dir /S /B /A:-D *.mp3 *.mp4 *.jpg | findstr /V /I /C:""""\\Microsoft\\"""" /C:""\\Windows\\""""", "C:\" )
My Scripts and Functions: V1  V2
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: RunCMD()

02 Jun 2020, 07:28

SKAN wrote:
02 Jun 2020, 06:51
hasantr wrote:
02 Jun 2020, 02:33
Hello there. This code runs on the cmd screen but does not result with RunCMD Where am I doing wrong.

Code: Select all

dir /S /B /A:-D *.mp3 *.mp4 *.jpg | findstr /V /I /C:""\\Microsoft\\"" /C:""\\Windows\\""
Change the WorkingDir as per your need:

Code: Select all

RunCmd( A_Comspec . " /c dir /S /B /A:-D *.mp3 *.mp4 *.jpg | findstr /V /I /C:""""\\Microsoft\\"""" /C:""\\Windows\\""""", "C:\" )
Thanks SKAN Now it happened. :bravo:
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: RunCMD()

02 Jun 2020, 11:37

A bit overquoting, eh? Try:

Code: Select all

RunCmd( A_Comspec . " /c dir /S /B /A:-D *.mp3 *.mp4 *.jpg | findstr /V /I /C:""\\Microsoft\\"" /C:""\\Windows\\""", "C:\" )
However results are not always the same, so I'm not sure which one is correct… [Edit]: Results are the same!
[Edit 2]: There is a typo in SKAN's post! I'm sure he ment:

Code: Select all

RunCmd( A_Comspec . " /c dir /S /B /A:-D *.mp3 *.mp4 *.jpg | findstr /V /I /C:""""\\Microsoft\\"""" /C:""""\\Windows\\""""", "C:\" )
P.s.: For the moment my speed test results are still pretty ambiguous, so I'm taking my time...
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

04 Jun 2020, 09:41

rommmcek wrote: However results are not always the same, so I'm not sure which one is correct… [Edit]: Results are the same!
[Edit 2]: There is a typo in SKAN's post! I'm sure he ment:

Code: Select all

RunCmd( A_Comspec . " /c dir /S /B /A:-D *.mp3 *.mp4 *.jpg | findstr /V /I /C:""""\\Microsoft\\"""" /C:""""\\Windows\\""""", "C:\" )
Thank you. :thumbup:

@hasantr : Amend your code with the above correct version.
My Scripts and Functions: V1  V2
xml123
Posts: 10
Joined: 01 Jun 2020, 18:55

Re: RunCMD()

20 Jun 2020, 02:22

Maybe it is irrelevant to this topic, but I'd like to know if this function can redirect a string variable to stdin?
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: RunCMD()

20 Jun 2020, 14:20

xml123 wrote:I'd like to know if this function can redirect a string variable to stdin?
If something works on command prompt, it should be possible w/ RunCMD too.
Do you have any example?
My Scripts and Functions: V1  V2
xml123
Posts: 10
Joined: 01 Jun 2020, 18:55

Re: RunCMD()

20 Jun 2020, 23:02

SKAN wrote: Do you have any example?
I have a program that reads from stdin. In fact I don't know how to do this in command prompt. But there is an example in official document:

Code: Select all

ExecScript(Script, Wait:=true)
{
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec("AutoHotkey.exe /ErrorStdOut *")
    exec.StdIn.Write(script)
    exec.StdIn.Close()
    if Wait
        return exec.StdOut.ReadAll()
}
My problem is that the "Exec" method can write stdin and get stdout while the "Run" method can hide prompt window. But there seems to be no way to do two things in the meantime. I don't want to use temp file or clipboard. I found your function can get stdout elegantly so I ask if you have any idea about dealing with stdin. That's why I said "may be it is irrelevant".
burque505
Posts: 1736
Joined: 22 Jan 2017, 19:37

Re: RunCMD()

21 Jun 2020, 08:49

@xml123, I've been trying to figure out where I found this code, cli_stdio.ahk. I can't even find it anywhere except my own file system, and it is totally undocumented. Might this work for your purposes?

Code: Select all

cli_stdout(chars="",codepage="") {
	global hStdOutRd
    if (codepage="")
         codepage:=A_FileEncoding
    fout:=FileOpen(hStdOutRd, "h", codepage)
    if (IsObject(fout) and fout.AtEOF=0)
      return fout.Read()
   return ""
}

cli_stdin(sInput="",codepage="") {
   global  hStdInWr
    if (codepage="")
         codepage:=A_FileEncoding 
      If   sInput <>
      FileOpen(hStdInWr, "h", codepage).Write(sInput)
}

cli_close() {
	global  hStdInWr, hStdOutRd
	DllCall("CloseHandle","Ptr",hStdInWr)
	DllCall("CloseHandle","Ptr",hStdOutRd)
}

cli_createprocess(sCmd, sDir="")
{
   global  hStdInWr, hStdOutRd
   
   DllCall("CreatePipe","Ptr*",hStdInRd,"Ptr*",hStdInWr,"Uint",0,"Uint",0)
   DllCall("CreatePipe","Ptr*",hStdOutRd,"Ptr*",hStdOutWr,"Uint",0,"Uint",0)
   DllCall("SetHandleInformation","Ptr",hStdInRd,"Uint",1,"Uint",1)
   DllCall("SetHandleInformation","Ptr",hStdOutWr,"Uint",1,"Uint",1)
   if A_PtrSize=4
      {
      VarSetCapacity(pi, 16, 0)
      sisize:=VarSetCapacity(si,68,0)
      NumPut(sisize,    si,  0, "UInt")
      NumPut(0x100,     si, 44, "UInt")
      NumPut(hStdInRd , si, 56, "Ptr")
      NumPut(hStdOutWr, si, 60, "Ptr")
      NumPut(hStdOutWr, si, 64, "Ptr")
      }
   else if A_PtrSize=8
      {
      VarSetCapacity(pi, 24, 0)
      sisize:=VarSetCapacity(si,96,0)
      NumPut(sisize,    si,  0, "UInt")
      NumPut(0x100,     si, 60, "UInt")
      NumPut(hStdInRd , si, 80, "Ptr")
      NumPut(hStdOutWr, si, 88, "Ptr")
      NumPut(hStdOutWr, si, 96, "Ptr")
      }

   DllCall("CreateProcess", "Uint", 0, "Ptr", &sCmd, "Uint", 0, "Uint", 0, "Int", True, "Uint", 0x08000000, "Uint", 0, "Ptr", sDir ? &sDir : 0, "Ptr", &si, "Ptr", &pi)
   DllCall("CloseHandle","Ptr",NumGet(pi,0))
   DllCall("CloseHandle","Ptr",NumGet(pi,A_PtrSize))
   DllCall("CloseHandle","Ptr",hStdOutWr)
   DllCall("CloseHandle","Ptr",hStdInRd)
}
Here's what I have so for stdin & stdout. I don't like (at all) the fact that I had to use a sleep command to let the 'dir' command finish.
The "`r`n" in

Code: Select all

cli_stdin("dir" . "`r`n")
is a faux "Enter" key.

Code: Select all

#SingleInstance, Force
#Include cli_stdio.ahk

cli_createprocess("cmd.exe")
cli_stdin("dir" . "`r`n")
sleep 1000
out .= cli_stdout()
cli_close()
msgbox %out%
ExitApp
Regards,
burque505
garry
Posts: 3764
Joined: 22 Dec 2013, 12:50

Re: RunCMD()

21 Jun 2020, 09:45

Is it possible to copy DOS window , example I run wget.exe and then see later in variable what happened ?
A small example for DOS command ( dir or cmd-help) , result in variable ( clipboard ) :

Code: Select all

clipboard=
Run,%comspec% /c dir|clip,, Min
;Run,%comspec% /c cmd /?|clip,, Min
ClipWait
msgbox,%clipboard%
return
burque505
Posts: 1736
Joined: 22 Jan 2017, 19:37

Re: RunCMD()

21 Jun 2020, 10:39

Maybe this?

Code: Select all

strStdOut := ""
objShell:=ComObjCreate("WScript.Shell")
command := "cmd /c ping autohotkey.com"
objExec:=objExec:=objShell.Exec(command)
while,!objExec.StdOut.AtEndOfStream
{
strStdOut.=objExec.StdOut.readline()"`r`n"
}
msgbox %strStdOut%
Regards,
burque505
garry
Posts: 3764
Joined: 22 Dec 2013, 12:50

Re: RunCMD()

21 Jun 2020, 10:53

@burque505 thank you ,
this works also with clip , but not with wget.exe ( or also other commandline programs )

Code: Select all

runwait,cmd /c ping -n 3 -w 1000 autohotkey.com|clip,,min
ClipWait
msgbox,%clipboard%
exitapp

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: Google [Bot] and 237 guests