【问题标题】:Capture CMD output with AutoHotkey使用 AutoHotkey 捕获 CMD 输出
【发布时间】:2021-09-28 03:12:43
【问题描述】:

我正在尝试使用 AutoHotkey 读取 Windows CMD 的标准输出。例如,我想将 AHK 中 setconsole 命令的输出存储在一个变量中。我已经在不久前实现了它,这让我更加困惑为什么它现在不起作用。
在 AHK 论坛中,有一个关于 CMDret 的相当 old thread,这是一个基于 DLL 的功能,可以完全按照我的意愿行事。第一个问题是为它找到一个有效的下载,因为帖子中的所有链接都已失效。 Google 给了我another site,托管 v3.1.2。尽管似乎有一个较新的版本(v3.2.1 分别为 4d Beta),但我检查了它并测试了一个简单的示例:

msgbox % CMDret(COMSPEC " /C set")
CMDret(CMD) 
{ 
  VarSetCapacity(StrOut, 10000) 
  RetVal := DllCall("cmdret.dll\RunReturn", "str", CMD, "str", StrOut)
  Return, %StrOut%
}

不幸的是,MsgBox 没有任何内容。然后我检查了RetVal,它的值为 0;并且随附的自述文件说:

如果函数失败,返回值为零。

再往下说:

注意:只有 32 位控制台应用程序目前可以使用 此 dll 版本的 CMDret(v3.1.2 或更低版本)。需要调用 command.com 可能不会产生任何输出并且可能会崩溃。避免 我在下载中包含了一个名为“cmdstub.exe”的文件(在 Win9x 文件夹)。调用16位时应使用此文件 控制台应用程序以启用返回输出。

总之,我不确定问题出在哪里。我的机器在 64 位上运行。但是自述文件中的相应条款应该只排除 16 位系统还是只包括 32 位系统?
如果计算架构可能不是问题,那可能是什么?

我正在寻找的是以下之一:

  1. 我可以解决这个问题并继续使用 v3.1.2 吗?
  2. 有没有人可以查看更新版本的工作源(甚至是本地副本)?
  3. 还有其他方法 [库、.ahk 代码等] 可以用于我的目的吗? (最好是类似的,因为 CMDret 看起来很简单)

【问题讨论】:

  • 嘿! ;) 你还需要答案吗?,我只是浏览了一下,我发现了这个问题,它可以完全在 ahk 中完成,我会尽可能发布答案......我目前正在旅行......跨度>

标签: cmd autohotkey


【解决方案1】:

如果您不需要实时输出,您可以使用 cmd 框本身保存自己的文本文件,然后您可以让 autohotkey 检测控制台的 PID 何时完成(使用返回的 runwait 并读取输出文件到内存中。

因此您可以在运行命令中执行此操作(只是一个常规的 cmd 参数):

ipconfig > myoutput.txt
exit

现在您有了一个包含 ipconfig 输出的文本文件。

或 你可以做同样的事情,但不是输出到文本文件,而是输出到剪贴板,如下所示:

ipconfig | clip

然后,由于输出在剪贴板上,您可以轻松地将其抓取到自动热键中。

【讨论】:

  • 我在这里也发现了一个安全问题。由于我正在处理敏感数据,我不希望我的密码出现在文件或剪贴板中。另外,我需要实时捕获流的能力。我认为使用这些方法会比较复杂。
  • 好吧,当然,您必须在脚本中处理它。如果它很敏感,那么您就不能使用剪贴板,因为其他东西可能正在跟踪它。但是,如果您输出到一个文本文件,并将该文件加载到内存中,那么您可以擦除该文本文件并将其删除。我在自己的一些应用程序中这样做,它就像一个魅力。只需三四行自动热键代码即可。
  • 文件也可以被任何其他进程访问。当然,该文件不存在很长时间,也许我有点偏执,但仍有机会我不想抓住。特别是因为 AHK 只删除文件,这不会使数据不可读。
  • 嗯...如果您在删除之前擦除内容(使用fileopen)。
  • 这太棒了!我正在使用此命令将我的 WAN IP 地址保存到变量中:nslookup myip.opendns.com resolver1.opendns.com | find /i "address" | find /v "208.67.222.222" | clip
【解决方案2】:

截至 2019 年 11 月,新推荐的 2 种做法 - https://www.autohotkey.com/docs/FAQ.htm#output

如何检索命令行操作的输出?

测试表明,由于文件缓存,对于相对较小的输出,临时文件可以非常快。事实上,如果文件在使用后立即删除,它通常不会真正写入磁盘。例如:

RunWait %ComSpec% /c dir > C:\My Temp File.txt
FileRead, VarToContainContents, C:\My Temp File.txt
FileDelete, C:\My Temp File.txt

为避免使用临时文件(尤其是在输出很大的情况下),请考虑使用 Shell.Exec() 方法,如 Run 命令的示例所示。

运行命令示例 - https://www.autohotkey.com/docs/commands/Run.htm#StdOut:

MsgBox % RunWaitOne("dir " A_ScriptDir)


RunWaitOne(command) {
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec(ComSpec " /C " command)
    return exec.StdOut.ReadAll()
}

注意:后一种方法 (shell.Exec) 将导致快速显示 cmd 窗口。 您可以通过将这些行放在脚本顶部来减少其出现的持续时间,这也将导致闪烁仅在您第一次调用 cmd 命令时发生一次。来自https://autohotkey.com/board/topic/92032-how-to-hide-a-wscript-comspec-window/

;Following 2 lines : the cmd window will flash only once and quickly
DllCall("AllocConsole")
WinHide % "ahk_id " DllCall("GetConsoleWindow", "ptr")

【讨论】:

    【解决方案3】:

    这个脚本怎么样,StdoutToVar
    它支持 64 位控制台。 http://www.autohotkey.com/board/topic/15455-stdouttovar/page-7

    【讨论】:

    【解决方案4】:

    这已经困扰我一段时间了 - 终于成功了!

    唯一的先决条件是 MS sqlcmd.exe,一个名为 AHK_Dev 的数据库 当然还有 AHK_DBA 在您希望使用它时读取该值。

    PS。确保将 {yourDir} 和 {yourServer} 替换为您自己的值!

    USE AHK_DEV
    CREATE TABLE [dbo].[AHK_DOS]([dos_out] [varchar](max) NULL) ON [PRIMARY];
    insert into ahk_dos select 'empty'
    

    创建以下脚本...将其命名为 dos_out.bat

    @echo off
    if "%1" == "" ( 
        set v_cmd=""
    ) else (
        set v_cmd=%1
    )
    set v_cmd=%v_cmd:~1,-1%
    SETLOCAL ENABLEDELAYEDEXPANSION
    if "!v_cmd!" == "" (
        set v_cmd="echo ... %COMPUTERNAME% %USERNAME% %DATE% %TIME%"
        set v_cmd=!v_cmd:~1,-1!
    )
    set v_data=""
    FOR /F "usebackq delims=¬" %%i in (`!v_cmd!`) do (
        set v_data="!v_data:~1,-1!%%i~"
    )
    set q_cmd="set nocount on;update ahk_dos set dos_out=N'!v_data:~1,-1!'"
    "{yourDir}\Microsoft SQL Server\90\Tools\Binn\sqlcmd.exe" -S {yourServer} -E -d ahk_dev -Q !q_cmd! -W 
    set q_cmd="set nocount on;select len(dos_out) as out_len, dos_out from ahk_dos"
    "{yourDir}\Microsoft SQL Server\90\Tools\Binn\sqlcmd.exe" -S {yourServer} -E -d ahk_dev -Q !q_cmd! -W -w 8000
    pause
    

    您可以使用...从 AHK 运行它

    dosCmd2db(c) {
    runwait, {yourDir\}dos_out.bat "%c%", , , dospid
    msgbox %dospid% closed
    }
    dosCmd2db("")
    dosCmd2db("echo This is a test")
    dosCmd2db("dir")
    

    由于每次都更新同一个字段,您显然需要在每个字段之间做一些事情以使此示例有用!

    试一试,告诉我你的进展情况

    问候,杰夫

    【讨论】:

    • 呃!据我了解,这将创建和写入/读取本地数据库?这听起来非常广泛。此外,我的要求之一是其他进程无法访问任何数据,因为我想将密码作为参数传递。我不希望它们出现在数据库中。
    【解决方案5】:

    只是对@amynbe 答案的更新。

    MsgBox % RunWaitOne("dir " A_ScriptDir)
    
    
    RunWaitOne(command) {
        shell := ComObjCreate("WScript.Shell")
        exec := shell.Exec(ComSpec " /C " command)
        return exec.StdOut.ReadAll() }
    

    注意:后一种方法(shell.Exec) 将导致快速显示 cmd 窗口。你可以减少 > 通过将这些行放在 > 你的脚本,这也会导致闪烁只发生一次 > 第一次调用 cmd 命令时。

    您可以在下面执行此操作以隐藏 cmd 并避免闪烁。

    MsgBox % RunWaitOne("dir " A_ScriptDir)
    
    
    RunWaitOne(command) {
      DetectHiddenWindows On
      Run %ComSpec%,, Hide, pid
      WinWait ahk_pid %pid%
      DllCall("AttachConsole", "UInt", pid)
    
      shell := ComObjCreate("WScript.Shell")
      exec := shell.Exec(ComSpec " /C " command)
    
      DllCall( "FreeConsole" )
        return exec.StdOut.ReadAll()
    }
    

    【讨论】:

      【解决方案6】:

      我在以下位置找到了适用于 AutoHotKey L 64bit 的仅脚本解决方案: http://www.autohotkey.com/board/topic/67687-ahkahk-lusing-wsh-to-interact-with-command-line-progs/

      玩了一会儿之后,我能够捕获我使用 DOS 类型命令列出的 40k 文本文件的整个输出。有一个演示展示了如何与 time 命令交互,如果您需要与 dos 命令或批处理脚本进行有限的双向交互,这很好。

      【讨论】:

      • 我同时也找到了那个。不幸的是,它总是会产生一个窗口,我想避免这种情况,特别是因为我正在调用带有密码等敏感数据的实用程序。
      猜你喜欢
      • 1970-01-01
      • 2013-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-03
      • 2019-02-23
      • 2014-07-26
      • 1970-01-01
      相关资源
      最近更新 更多