ChatGPT解决这个技术问题 Extra ChatGPT

使用 MS 批处理文件将程序的输出分配给变量

我需要使用 MS 批处理文件将程序的输出分配给变量。

所以在 GNU Bash shell 中我会使用 VAR=$(application arg0 arg1)。我需要使用批处理文件在 Windows 中进行类似的行为。

set VAR=application arg0 arg1 之类的东西。

类似问题

如何将命令输出设置为批处理文件中的变量

如何在 windows 的变量中获取命令的结果?

使用 Windows 批处理文件中的命令结果设置变量的值

将命令的输出设置为变量(使用管道)

将命令输出分配给批处理文件中的变量


W
Wolf

一种方法是:

application arg0 arg1 > temp.txt
set /p VAR=<temp.txt

另一个是:

for /f %%i in ('application arg0 arg1') do set VAR=%%i

请注意,%%i 中的第一个 % 用于转义其后的 %,并且在批处理文件而不是命令行中使用上述代码时需要。想象一下,您的 test.bat 具有以下内容:

for /f %%i in ('c:\cygwin64\bin\date.exe +"%%Y%%m%%d%%H%%M%%S"') do set datetime=%%i
echo %datetime%

这是一个很棒的技巧,我想知道为什么它不适用于管道
这仅适用于单行文本的输出(第一个换行符后省略的后续行)。
@Machta 必须在括号中的表达式内使用 ^ 符号对管道进行转义。示例:for /f "tokens=3" %%i in ('route print ^| findstr "\<0.0.0.0\>"') do set "myVar=%%i"
不要为带空格的行工作。例如:for /f %%i in ('ver') 设置 VAR=%%i。正如@Renat 所写,应该添加“tokens=*”
@GroovyCakes 您关于输出中多行的问题由 this answer 在重复问题上回答
C
Community

作为 this previous answer 的补充,管道可以在 for 语句中使用,由插入符号转义:

    for /f "tokens=*" %%i in ('tasklist ^| grep "explorer"') do set VAR=%%i

两个重要的点:使用标记来捕获和插入符号来逃脱管道。
在 CLI 上工作的等效版本,可以复制粘贴以便于修改:for /f "tokens=*" %i in ('tasklist ^| findstr explorer') do @echo %i 但一般来说,usebackq 应该用于处理复杂的命令。
需要令牌来处理输出中的空格。
引号对我也有效,例如:for /f "tokens=*" %%i in ('"tasklist | grep explorer"') do set VAR=%%i。如果命令本身没有引号,对我来说更容易。
K
KyleMit

执行以下操作时:

for /f %%i in ('application arg0 arg1') do set VAR=%%i

我收到了错误:

%%i was unexpected at this time.

为了解决这个问题,我改为使用单个 % 符号,如下所示:

for /f %i in ('application arg0 arg1') do set VAR=%i

概括:

在批处理文件中使用 %%

在批处理文件之外(在命令行上)使用 %


在批处理文件中需要 %%,在命令行上的批处理文件之外需要 %
g
ghostdog74

@OP,您可以使用 for loops 来捕获程序的返回状态,如果它输出的不是数字


j
jeb

您可以使用批处理宏来简单地捕获命令输出,有点像 bash shell 的行为。

宏的用法很简单,看起来像

%$set% VAR=application arg1 arg2

它甚至适用于管道

%$set% allDrives="wmic logicaldisk get name /value | findstr "Name""

宏像数组一样使用变量并将每一行存储在单独的索引中。
%$set% allDrives="wmic logicaldisk 的示例中,将创建以下变量:

allDrives.Len=5
allDrives.Max=4
allDrives[0]=Name=C:
allDrives[1]=Name=D:
allDrives[2]=Name=F:
allDrives[3]=Name=G:
allDrives[4]=Name=Z:
allDrives=<contains the complete text with line feeds>

要使用它,了解宏本身的工作原理并不重要。

完整的例子

@echo off
setlocal

call :initMacro

%$set% ipOutput="ipconfig"
call :ShowVariable ipOutput
echo First line is %ipOutput[0]%

echo( 
%$set% driveNames="wmic logicaldisk get name /value | findstr "Name""
call :ShowVariable driveNames

exit /b

:ShowVariable
setlocal EnableDelayedExpansion
for /L %%n in (0 1 !%~1.max!) do (
    echo %%n: !%~1[%%n]!
)
echo(
exit /b

:initMacro
if "!!"=="" (
    echo ERROR: Delayed Expansion must be disabled while defining macros
    (goto) 2>nul
    (goto) 2>nul
)
(set LF=^
%=empty=%
)
(set \n=^^^
%=empty=%
)

set $set=FOR /L %%N in (1 1 2) dO IF %%N==2 ( %\n%
    setlocal EnableDelayedExpansion                                 %\n%
    for /f "tokens=1,* delims== " %%1 in ("!argv!") do (            %\n%
        endlocal                                                    %\n%
        endlocal                                                    %\n%
        set "%%~1.Len=0"                                            %\n%
        set "%%~1="                                                 %\n%
        if "!!"=="" (                                               %\n%
            %= Used if delayed expansion is enabled =%              %\n%
                setlocal DisableDelayedExpansion                    %\n%
                for /F "delims=" %%O in ('"%%~2 | findstr /N ^^"') do ( %\n%
                if "!!" NEQ "" (                                    %\n%
                    endlocal                                        %\n%
                    )                                               %\n%
                setlocal DisableDelayedExpansion                    %\n%
                set "line=%%O"                                      %\n%
                setlocal EnableDelayedExpansion                     %\n%
                set pathExt=:                                       %\n%
                set path=;                                          %\n%
                set "line=!line:^=^^!"                              %\n%
                set "line=!line:"=q"^""!"                           %\n%
                call set "line=%%line:^!=q""^!%%"                   %\n%
                set "line=!line:q""=^!"                             %\n%
                set "line="!line:*:=!""                             %\n%
                for /F %%C in ("!%%~1.Len!") do (                   %\n%
                    FOR /F "delims=" %%L in ("!line!") Do (         %\n%
                        endlocal                                    %\n%
                        endlocal                                    %\n%
                        set "%%~1[%%C]=%%~L" !                      %\n%
                        if %%C == 0 (                               %\n%
                            set "%%~1=%%~L" !                       %\n%
                        ) ELSE (                                    %\n%
                            set "%%~1=!%%~1!!LF!%%~L" !             %\n%
                        )                                           %\n%
                    )                                               %\n%
                    set /a %%~1.Len+=1                              %\n%
                )                                                   %\n%
            )                                                       %\n%
        ) ELSE (                                                    %\n%
            %= Used if delayed expansion is disabled =%             %\n%
            for /F "delims=" %%O in ('"%%~2 | findstr /N ^^"') do ( %\n%
                setlocal DisableDelayedExpansion                    %\n%
                set "line=%%O"                                      %\n%
                setlocal EnableDelayedExpansion                     %\n%
                set "line="!line:*:=!""                             %\n%
                for /F %%C in ("!%%~1.Len!") DO (                   %\n%
                    FOR /F "delims=" %%L in ("!line!") DO (         %\n%
                        endlocal                                    %\n%
                        endlocal                                    %\n%
                        set "%%~1[%%C]=%%~L"                        %\n%
                    )                                               %\n%
                    set /a %%~1.Len+=1                              %\n%
                )                                                   %\n%
            )                                                       %\n%
        )                                                           %\n%
        set /a %%~1.Max=%%~1.Len-1                                  %\n%
)                                                                   %\n%
    ) else setlocal DisableDelayedExpansion^&set argv=

goto :eof

a
akf

假设您的应用程序的输出是数字返回码,您可以执行以下操作

application arg0 arg1
set VAR=%errorlevel%

不幸的是,输出是一个字符串。
好的。我将保留它以供后代使用,但请查看@jdigital 的链接,该链接讨论了将管道输出到临时文件。
程序对 stdout 和 stderr 的输出不同于它的整数返回值。一个程序既可以像上面的例子一样返回一个整数值,同时也可以向控制台发送一个字符串(或重定向到一个文件或其他地方)。它们并不相互排斥,是两个不同的概念。
C
Community

除了答案之外,您不能直接在 for 循环的 set 部分中使用输出重定向运算符(例如,如果您想对用户隐藏 stderror 输出并提供更好的错误消息)。相反,您必须使用插入字符 (^) 转义它们:

for /f %%O in ('some-erroring-command 2^> nul') do (echo %%O)

参考:Redirect output of command in for loop of batch script


K
Kurt Van den Branden
@echo off
SETLOCAL ENABLEDELAYEDEXPANSION

REM Prefer backtick usage for command output reading:
REM ENABLEDELAYEDEXPANSION is required for actualized
REM  outer variables within for's scope;
REM within for's scope, access to modified 
REM outer variable is done via !...! syntax.

SET CHP=C:\Windows\System32\chcp.com

FOR /F "usebackq tokens=1,2,3" %%i IN (`%CHP%`) DO (
    IF "%%i" == "Aktive" IF "%%j" == "Codepage:" (
        SET SELCP=%%k
        SET SELCP=!SELCP:~0,-1!
    )
)
echo actual codepage [%SELCP%]

ENDLOCAL

(plus1) 用于反引号解释
J
Ja Vy

我编写了每 5 秒 ping 一次 google.com 并使用当前时间记录结果的脚本。在这里您可以找到变量“commandLineStr”的输出(带索引)

@echo off

:LOOPSTART

echo %DATE:~0% %TIME:~0,8% >> Pingtest.log

SETLOCAL ENABLEDELAYEDEXPANSION
SET scriptCount=1
FOR /F "tokens=* USEBACKQ" %%F IN (`ping google.com -n 1`) DO (
  SET commandLineStr!scriptCount!=%%F
  SET /a scriptCount=!scriptCount!+1
)
@ECHO %commandLineStr1% >> PingTest.log
@ECHO %commandLineStr2% >> PingTest.log
ENDLOCAL

timeout 5 > nul

GOTO LOOPSTART

n
npocmaka

一些宏将命令的输出设置为变量/

直接在命令提示符下

c:\>doskey assign=for /f "tokens=1,2 delims=," %a in ("$*") do @for /f "tokens=* delims=" %# in ('"%a"') do @set "%b=%#"

c:\>assign WHOAMI /LOGONID,my-id

c:\>echo %my-id%

带参数的宏

由于这个 macro accepts arguments 作为一个函数,我认为它是在批处理文件中使用的最简洁的宏:

@echo off

::::: ---- defining the assign macro ---- ::::::::
setlocal DisableDelayedExpansion
(set LF=^
%=EMPTY=%
)
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

::set argv=Empty
set assign=for /L %%n in (1 1 2) do ( %\n%
   if %%n==2 (%\n%
      setlocal enableDelayedExpansion%\n%
      for /F "tokens=1,2 delims=," %%A in ("!argv!") do (%\n%
         for /f "tokens=* delims=" %%# in ('%%~A') do endlocal^&set "%%~B=%%#" %\n%
      ) %\n%
   ) %\n%
) ^& set argv=,

::::: -------- ::::::::


:::EXAMPLE
%assign% "WHOAMI /LOGONID",result
echo %result%

FOR /F 宏

不像前面的宏那么容易阅读。

::::::::::::::::::::::::::::::::::::::::::::::::::
;;set "{{=for /f "tokens=* delims=" %%# in ('" &::
;;set "--=') do @set ""                        &::
;;set "}}==%%#""                               &::
::::::::::::::::::::::::::::::::::::::::::::::::::

:: --examples

::assigning ver output to %win-ver% variable
%{{% ver %--%win-ver%}}%
echo 3: %win-ver%


::assigning hostname output to %my-host% variable
%{{% hostname %--%my-host%}}%
echo 4: %my-host%

使用临时文件的宏

更容易阅读,如果你有一个 SSD 驱动器,它不会那么慢,但它仍然会创建一个临时文件。

@echo off

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;;set "[[=>"#" 2>&1&set/p "&set "]]==<# & del /q # >nul 2>&1" &::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

chcp %[[%code-page%]]%
echo ~~%code-page%~~

whoami %[[%its-me%]]%
echo ##%its-me%##