【问题标题】:Windows Batch help in setting a variable from command output [duplicate]Windows Batch帮助从命令输出设置变量[重复]
【发布时间】:2015-12-29 08:51:23
【问题描述】:

我需要运行一个简单的查找命令并将输出重定向到 Windows 批处理文件中的变量。

我试过这个:

set file=ls|find ".txt"
echo %file%

但它不起作用。

如果我运行这个命令,它可以正常工作:

set file=test.txt
echo %file%  

所以很明显我的命令输出没有被设置为我的变量。任何人都可以帮忙吗?谢谢

【问题讨论】:

  • +1,关于一个非常有用的 Unix 功能显然在 DOS 中不可用的好问题...
  • DCookie,你至少可以在 Windows 中做到。阅读for /f

标签: batch-file


【解决方案1】:

我刚刚了解了如何使用带有管道的命令,这是我的命令(提取 svn repo 的头部修订版):

SET SVN_INFO_CMD=svn info http://mySvnRepo/MyProjects
FOR /f "tokens=1 delims=" %%i IN ('%SVN_INFO_CMD% ^| find "Revision"') DO echo %%i

【讨论】:

  • 这正是我想要的。尤其是管道!
【解决方案2】:

首先,您对问题的期望似乎在 UNIX shell 中是不可能的。 shell 怎么知道ls|find foo 是命令而test.txt 不是?在这里执行什么?这就是为什么 UNIX shell 对这些事情有反引号的原因。无论如何,我离题了。

您不能在 shell 中将环境变量设置为多行字符串。所以我们现在遇到了一个问题,因为ls 的输出不太合适。

不过,您真正想要的是所有文本文件的列表,对吧?根据您的需要,这很容易做到。所有这些示例的主要部分是 for 循环,它遍历一组文件。

如果您只需要对每个文本文件执行一个操作:

for %%i in (*.txt) do echo Doing something with "%%i"

这甚至适用于带有空格的文件名,并且不会错误地捕获名称中间只有 .txt 的文件,例如 foo.txt.bar。只是指出您的方法并不像您希望的那样漂亮。

无论如何,如果你想要一个文件列表,你可以使用一个小技巧来创建数组,或者类似的东西:

setlocal enabledelayedexpansion
set N=0
for %%i in (*.txt) do (
    set Files[!N!]=%%i
    set /a N+=1
)

在此之后,您将拥有许多环境变量,命名为Files[0]Files[1] 等。每个环境变量都包含一个文件名。你可以循环使用

for /l %%x in (1,1,%N%) do echo.!Files[%%x]!

(请注意,我们在这里输出了一个多余的新行,我们可以将其删除,但需要多行代码:-))

然后,如果您愿意,您可以构建一长串文件名。您可能会认出这种模式:

setlocal enabledelayedexpansion
set Files=
for %%i in (*.txt) do set Files=!Files! "%%i"

现在我们有一个非常长的文件名行。随心所欲地使用它。有时这对于将一堆文件传递给另一个程序很方便。

但请记住,批处理文件的最大行长度约为 8190 个字符。所以这限制了你可以在一行中拥有的东西的数量。是的,在一行中枚举一大堆文件可能会在这里溢出。

回到原点,批处理文件无法捕获命令输出。其他人之前已经注意到了。为此,您可以使用for /f

for /f %%i in ('dir /b') do ...

这将遍历命令返回的行,一路标记它们。可能不如反引号那么方便,但对于大多数用途来说足够接近和足够了。

默认情况下,令牌在空白处被分解,所以如果你有一个文件名“Foo bar”,那么你会突然在%%i 中只有“Foo”,在%%j 中只有“bar”。这可能会令人困惑,而这些事情是您不想使用for /f 来获取文件列表的主要原因。

如果与某些程序参数冲突,您也可以使用反引号代替撇号:

for /f "usebackq" %%i in (`echo I can write 'apostrophes'`) do ...

请注意,这也会标记化。您可以提供更多选择。它们在help for 命令中有详细说明。

【讨论】:

  • 感谢约翰内斯的详细解释。在我的示例中,我不会有此文件类型的多个实例。 (.txt) 只有一个文件。正如我在上面的评论中提到的,我有一个似乎可以工作的脚本: for %%i in (.txt) do set file=%%i (在控制台结果中它列出了唯一的 txt 文件名)但我已尝试在之后的行上回显变量,但它不起作用。我还尝试使用该变量复制文件,但它不起作用。设置后如何使用此变量?这是我的复制脚本: for %%i in (.txt) do set file=%%i copy %%ic:\temp\.
  • “首先,您对问题的期望似乎在 UNIX shell 中是不可能的。”这不是真的。当然,OP 的语法是错误的,但它在 Unix 中是可行的: TheVariable=ls|grep .txt 完全按照 OP 的要求工作。我发现这个问题是因为我需要 DOS 中的确切功能。我想这是 DOS shell 脑死亡的又一个例子
  • 如果 OP 期望它与在 UNIX shell 中一样工作,我期望反引号在那里。这就是为什么我不认为这是一种错误。至于脑死亡......为什么你甚至在 DOS 中需要这个?我已经好几年没见过 DOS 用户了。除此之外,我在答案中使用的几乎每一行都不会在 DOS 中运行,所以这基本上是一个有争议的问题。
  • 我想我的意思是,从这个问题的标题中我很清楚 OP 想要为命令的输出设置一个变量,这显然不能直接用 DOS 完成。至于为什么?有时,我们会被赋予完成事情的环境和要完成的任务。我一直使用它来构建基于生产数据库副本的开发 Oracle 数据库。一个 DOS 脚本可以完成任务 - 它必须这样做,因为没有 *nix 选项。
  • 好的,我知道你现在怎么做。不直观且非常尴尬,但可能,因为将变量设置为当前时间的这段代码显示:for /f "usebackq delims==" %i in (time /t) do @set thetime=%i(时间 /t 周围有反引号,但我不知道如何让它们显示在此评论块中:-|
【解决方案3】:

set 命令有/p 选项,告诉它从标准输入中读取一个值。不幸的是,它不支持管道,但它支持从现有文件的第一行读取值。

因此,要将变量设置为第一个 *.txt 文件的名称,您可以执行以下操作:

dir /b *.txt > filename.tmp
set /p file=< filename.tmp
del /q filename.tmp

重要的是不要在= 之前甚至之后添加空格。

P。 S.没有fors,没有tokens

【讨论】:

    【解决方案4】:

    这是一个批处理文件,它将返回find 输出的最后一项:

    @echo off
    
    ls | find ".txt" > %temp%\temp.txt
    for /f %%i in (%temp%\temp.txt) do set file=%%i
    del %temp%\temp.txt
    echo %file%
    

    for 具有解析命令输出的语法for /f "usebackq",但它无法处理命令中的管道,因此我已将输出重定向到临时位置。

    鉴于您可以访问ls,我强烈建议您考虑使用更好的批处理语言,例如bash,甚至是pythonruby 等脚本语言。即使是 bash 也会比 cmd 脚本提高 20 倍。

    【讨论】:

    • 应该能够通过将管道写为 ^| 来处理管道而不仅仅是| .
    【解决方案5】:

    简短的回答是:不要!

    Windows shell env var 最多可以容纳 32 Kb,在其中保存程序的输出是不安全的。 这就是为什么你不能。在批处理脚本中,您必须采用另一种编程风格。如果你需要所有的输出 从程序中,然后将其保存到文件中。如果您只需要检查某些属性,则将输出通过管道传输到 一个执行检查并使用错误级别机制的程序:

    @echo off
    
    type somefile.txt | find "somestring" >nul
    if %errorlevel% EQU 1 echo Sorry, not found!
    REM Alternatively:
    if errorlevel 1 echo Sorry, not found!
    

    不过,使用 Perl 风格的逻辑运算符会更优雅:

    @echo off
    
    (type somefile.txt | find "somestring" >nul) || echo Sorry, not found!
    

    【讨论】:

      【解决方案6】:

      它在 DOS 中不可用,但在 Windows 控制台中,有 for 命令。只需在命令提示符下键入“帮助”即可查看所有选项。要设置单个变量,您可以使用:

      for /f %%i in ('find .txt') do set file=%%i
      

      请注意,这仅适用于从“find .txt”返回的第一行,因为默认情况下 windows 仅展开变量一次。您必须启用延迟扩展,如 here 所示。

      【讨论】:

      • 这会将file 设置为最后 行文本,因为它将file 设置为每次执行的每一行。这意味着file 确实设置为第一行,但随后立即被第二行覆盖。这一直持续到最后一行,它仍然是file 中设置的值,因为它没有被覆盖。同样find .txt 会给你一个错误,因为find 需要一个文本样本来搜索,以及一个文本流来搜索,比如find "hello" *.txtecho %data% | find "hello"find .txt 会出错,因为 FIND 没有要搜索的字符串。
      【解决方案7】:

      您实际上是在列出 .txt 文件。这样,您可以使用 for 循环来覆盖 dir cmd 例如

      for /f "tokens=*" %%i in ('dir /b *.txt') do set file=%%i
      

      或者,如果您更喜欢使用 ls,则无需通过管道查找。

      for /f "tokens=*" %%i in ('ls *.txt') do set file=%%i
      

      【讨论】:

      • 请不要将for /f 用于此类事情。你可以做到for %%i in (*.txt) 非常好,甚至不会像你的代码当前那样遇到文件名中的空格问题。
      • 那又怎样?不代表不能用。
      • 这是不必要的,并且在这种情况下会产生可以避免的问题。
      • 很多东西都是不需要批量的,不只是这个。
      • 感谢您反馈几个后续问题。这似乎可以作为我的脚本工作: for %%i in (.txt) do set file=%%i (在控制台结果中,它列出了唯一的 txt 文件名)但我尝试在上回显变量之后的线路,它没有工作。我还尝试使用该变量复制文件,但它不起作用。设置后如何使用此变量?这是我的复制脚本: for %%i in (.txt) do set file=%%i copy %%ic:\temp\
      【解决方案8】:

      从命令输出设置变量的示例:

      FOR /F "usebackq" %%Z IN ( `C:\cygwin\bin\cygpath "C:\scripts\sample.sh"` ) DO SET BASH_SCRIPT=%%Z
      
      c:\cygwin\bin\bash -c '. ~/.bashrc ; %BASH_SCRIPT%'
      

      另外,请注意,如果你想在 DOS shell 中测试 FOR 命令,那么你只需要使用 %Z 而不是 %%Z,否则会报以下错误:

      %%Z was unexpected at this time.
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-24
        • 1970-01-01
        • 1970-01-01
        • 2014-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多