【问题标题】:Assign output of a program to a variable using a MS batch file使用 MS 批处理文件将程序的输出分配给变量
【发布时间】:2011-01-20 08:46:37
【问题描述】:

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

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

类似set VAR=application arg0 arg1

【问题讨论】:

标签: batch-file cmd variable-assignment windows-console


【解决方案1】:

一种方法是:

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 "\&lt;0.0.0.0\&gt;"') do set "myVar=%%i"
  • 不适用于带空格的行。例如:for /f %%i in ('ver') 设置 VAR=%%i。正如@Renat 所写,应添加“tokens=*”
  • @GroovyCakes 您关于输出中多行的问题由this answer 在重复问题上回答
【解决方案2】:

作为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。如果命令本身没有引号,对我来说更容易。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    在执行中:for /f %%i in ('application arg0 arg1') do set VAR=%%i 我收到错误:%%i 此时是意外的。 作为修复,我必须在上面执行for /f %i in ('application arg0 arg1') do set VAR=%i

    【讨论】:

    • 在批处理文件中需要%%,在批处理文件之外需要%
    【解决方案5】:

    您可以使用批处理宏来简单地捕获命令输出,有点像 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
    

    【讨论】:

      【解决方案6】:

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

      application arg0 arg1
      set VAR=%errorlevel%
      

      【讨论】:

      • 不幸的是,输出是一个字符串。
      • 好的。我会为后代保留这个,但看看@jdigital 的链接,它讨论了将管道输出到临时文件。
      • 程序对stdout和stderr的输出与它的整数返回值不同。一个程序既可以像上面的例子一样返回一个整数值,同时也可以向控制台发送一个字符串(或重定向到一个文件或其他地方)。它们并不相互排斥,是两个不同的概念。
      【解决方案7】:

      除了答案之外,您不能直接在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

      【讨论】:

        【解决方案8】:
        @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) 用于反引号解释
        【解决方案9】:

        我编写了每 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
        

        【讨论】:

          【解决方案10】:

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

          直接在命令提示符下

          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%##
          

          【讨论】:

            猜你喜欢
            • 2013-04-18
            • 1970-01-01
            • 1970-01-01
            • 2019-05-12
            • 1970-01-01
            • 2021-02-04
            • 1970-01-01
            相关资源
            最近更新 更多