【问题标题】:Hidden features of Windows batch filesWindows批处理文件的隐藏功能
【发布时间】:2010-09-19 17:05:22
【问题描述】:

Windows 批处理文件有哪些鲜为人知但重要且有用的功能?

指南:

  • 每个答案一个功能
  • 提供功能的简短描述示例,而不仅仅是文档链接
  • 将答案限制在本机功能,即不需要其他软件,例如 Windows 资源工具包

澄清:我们在这里指的是由 cmd.exe 处理的脚本,这是 WinNT 变体的默认设置。

(另见:Windows batch files: .bat vs .cmd?

【问题讨论】:

    标签: windows batch-file hidden-features


    【解决方案1】:

    续行:

    call C:\WINDOWS\system32\ntbackup.exe ^
        backup ^
        /V:yes ^
        /R:no ^
        /RS:no ^
        /HC:off ^
        /M normal ^
        /L:s ^
        @daily.bks ^
        /F daily.bkf
    

    【讨论】:

    • ^ 实际上是一个引号字符。使用它,您可以引用 以便它们不会重定向输出。行尾的 ^ 也允许行继续。
    • 你能解释一下这个小脚本吗?
    • @furtelwart:这就像您将所有内容写入一行:call C:\WINDOWS\system32\ntbackup.exe backup /V:yes /R:no /RS:no /HC:off /M normal /L:s @daily.bks /F daily.bkf。要了解该行的所有参数,只需运行C:\WINDOWS\system32\ntbackup.exe /?
    • 关于这个话题的深入讨论,long commands split over multiple lines
    【解决方案2】:
    PUSHD path
    

    带你到path指定的目录。

    POPD
    

    带你回到你“推送”的目录。

    【讨论】:

    • 这也可以作为一个完整的堆栈,因此您可以将许多目录推送到堆栈上,然后继续弹出以回到原来的位置。
    • 运行 'cmd.exe' 然后输入 'help',然后输入 'help pushd' 或 'pushd /?'。
    • 如果你推送到 UNC 路径,它会自动为你映射一个驱动器,popd 会取消映射它。
    • 在 csh 中,如果我记得的话,一个没有 arg 的 pushd 只是循环了堆栈。但这在 cmd.exe 上不起作用,是吗?
    • cd不同,您还可以使用pushd在不同驱动器上的目录之间切换。 (如果您在 C:\Windows 中并希望在 D:\Games 中,则 pushd D:\Games 会将您带到那里,而不是输入 D:cd D:\Games。)
    【解决方案3】:

    不确定这在 batch 文件中会有多大用处,但在命令提示符中使用它是一个非常方便的命令:

    C:\some_directory> start .
    

    这将在“some_directory”文件夹中打开 Windows 资源管理器。

    我发现这非常节省时间。

    【讨论】:

    • 好吧,我也用它。我在 PATH 目录之一中有一个“open.cmd”文件,该文件中唯一的内容是“@start”。 ;)
    • 'explorer' 也和 'start' C:\some_directory>explorer 做同样的事情。
    • 我倾向于将其输入为 [ start "" 。 ] (为了清楚起见,括号),因为 start 有时对第一个参数是标题很挑剔。
    • 如果你碰巧在 Mac 上,open . 会做同样的事情。
    • start 不仅仅是打开当前文件夹。您可以将任何文件传递给它,它将使用配置的查看器打开它。给它一个 URL,你的默认浏览器就会打开,等等......
    【解决方案4】:

    我一直觉得每行都用关键字标记的 cmets 很难阅读:

    REM blah blah blah
    

    更容易阅读:

    :: blah blah blah
    

    【讨论】:

    • 我听说 :: 比 REM 更有效,因为 REM 尝试对它之后发生的内容进行环境变量扩展,但 :: 没有。
    • 其实::只是一个名字搞笑的标签;因此,如果您在块内(括号中)使用 :: 将不起作用,因为那里也不允许使用标签。 REM 当然在那里工作。
    • 请注意,rem 是已记录的关键字,而 :: 只是一个实现细节。虽然:: 不太可能停止工作,但通常建议不要依赖未记录的行为。
    • 您甚至可以使用goto : 跳转到标签。 :-)goto -) 也可以。
    【解决方案5】:

    变量子串:

    > set str=0123456789
    > echo %str:~0,5%
    01234
    > echo %str:~-5,5%
    56789
    > echo %str:~3,-3%
    3456
    

    【讨论】:

    • @furtelwart 听起来可能是批处理的座右铭
    • 这非常适合格式化日期(但是我发现的问题是 vista/7 和 xp 输出日期不同)
    • 请注意,不幸的是,这不能与 FOR 循环 (for %a in ...) 的局部变量结合使用,因为它们不像环境变量那样需要结束百分号;您必须首先将它们分配给环境变量(使用延迟扩展!),然后提取子字符串。
    • 如果你想对日期做一些事情(例如备份文件名),这是不可避免的丑陋。在目睹了 Windows 命令行的普遍丑陋之后,我明白了为什么 Windows 主要是点击 :) 虽然他们说新的 PowerShell 更好。
    【解决方案6】:

    The FOR command!虽然我讨厌编写批处理文件,但我很感激。

    FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k
    

    将解析 myfile.txt 中的每一行,忽略以分号开头的行,将每行的第二个和第三个标记传递给 for 正文,标记由逗号和/或空格分隔。 注意 for body 语句引用 %i 获取第二个标记,%j 获取第三个标记,以及 %k 获取第三个之后的所有剩余标记。

    您还可以使用它来迭代目录、目录内容等...

    【讨论】:

    • 我发现批处理文件的 FOR 循环有限且难以编写,但它们有时很有用。
    • 请原谅我的困惑,但是这到底是怎么被低估的呢?我想如果你不知道 FOR 循环,你就不会知道批处理脚本。
    • 利用与否,都是一种折磨。 (有些人会认为这是必要的邪恶。)
    • 我不得不使用它几次,而编写这种语法的人应该被解雇。从一门大炮。进入太阳。太糟糕了。
    • @CodingWithStyle:每次我需要在批处理文件中编写for循环时,脚本都会变成bash启动器并改用bash(或python)重写脚本:)
    【解决方案7】:

    我没有在脚本中乱扔 REM 或 :: 行,而是在每个脚本的顶部执行以下操作:

    @echo OFF
    goto :START
    
    Description of the script.
    
    Usage:
       myscript -parm1|parm2 > result.txt
    
    :START
    

    注意如何在不转义的情况下使用管道和重定向字符。

    【讨论】:

    • 如果你检查 %1 的“/?”会更酷然后您可以将该部分作为帮助文本回显。
    • 嗯,识字的批处理编程?!
    【解决方案8】:

    脚本所在的路径(带驱动器):~dp0

    set BAT_HOME=%~dp0
    echo %BAT_HOME%
    cd %BAT_HOME%
    

    【讨论】:

    • 我通常为此使用 %CD%。也许它并非在所有 DOS shell 版本中都可用?
    • %CD% 为当前目录,%~dp0 为运行脚本所在目录。
    • 另外,我认为 %CD% 在...XP 之前不存在,也许吧。我知道一些旧版本的 Windows 没有它。
    • 如果 bat 在另一个驱动器中,您应该使用 cd /d %BAT_HOME% 代替。不过,如果我没记错的话,这不适用于较旧的 DOS。
    • 如果您在网络路径上运行批处理,cd 或 pushd %BATH_HOME% 将不起作用。
    【解决方案9】:

    %~dp0 部分已经提到过,但实际上还有更多内容: ~ 之后的字符定义提取的信息。
    补丁文件名返回无字母结果
    d - 返回驱动器号
    p - 返回路径
    s - 返回短路径
    x - 返回文件扩展名
    所以如果你从c:\Temp\long dir name\文件夹中执行下面的脚本test.bat,

    @echo off
    echo %0
    echo %~d0
    echo %~p0
    echo %~dp0
    echo %~x0
    echo %~s0
    echo %~sp0
    

    你得到以下输出

    test
    c:
    \Temp\long dir name\
    c:\Temp\long dir name\
    .bat
    c:\Temp\LONGDI~1\test.bat
    \Temp\LONGDI~1\
    

    如果一个参数被传递到你的脚本中
    测试 c:\temp\mysrc\test.cpp
    %1 变量也可以进行相同的操作。

    但是 %0 的扩展结果取决于位置!
    在批处理的“顶层”,它扩展为当前批处理文件名。
    在函数(调用)中,它扩展为函数名。

    @echo off
    echo %0
    call :test
    goto :eof
    
    :test
    echo %0
    echo %~0
    echo %~n0
    

    输出是(批处理文件以 myBatch.bat 开始)

    myBatch.bat
    :test
    :test
    myBatch
    

    【讨论】:

      【解决方案10】:

      通过使用 CALL、EXIT /B、SETLOCAL 和 ENDLOCAL,您可以使用局部变量实现子例程。

      示例:

      @echo off
      
      set x=xxxxx
      call :sub 10
      echo %x%
      exit /b
      
      :sub
      setlocal
      set /a x=%1 + 1
      echo %x%
      endlocal
      exit /b
      

      这将打印出来

      11
      xxxxx
      

      即使 :sub 修改了 x。

      【讨论】:

      • 你应该使用 goto :eof 而不是 exit /b,做同样的事情,但这是更标准的方式。
      • 这个有标准吗? O_o
      • 我的错误。我以为您的意思是明确定义一个 :eof 标签并执行 goto 。我没有意识到每个批处理文件的末尾都有一个隐含的 :eof 标签。
      • 但是,如果您希望子例程设置错误级别,则需要使用 exit /b。例如:退出 /b 3
      • 我发现最好使用“exit /B”而不是“goto :eof”从子程序返回,“goto :eof”有一个问题,你可能会在以下情况下返回错误代码你想吞下它。例如,如果您使用“if exists someFile echo it's here”,如果 someFile 不存在,这将设置错误级别,但这并没有错,也不是您想要返回的错误代码(这就是“ goto :eof" 可以)。
      【解决方案11】:

      等待 N 秒的诡计(不是 cmd.exe 的一部分,但不是额外的软件,因为它与 Windows 一起提供),请参阅 ping 行。您需要 N+1 次 ping,因为第一个 ping 会立即发出。

          echo %time%
          call :waitfor 5
          echo %time%
          goto :eof
      :waitfor
          setlocal
          set /a "t = %1 + 1"
          >nul ping 127.0.0.1 -n %t%
          endlocal
          goto :eof
      

      【讨论】:

      • 更好的办法是把它放在一个像 sleep.bat 这样的文件中,这样你就省去了一遍又一遍地重写它的麻烦。
      • ...并将 sleep.bat 放在 PATH 环境变量的某个目录中
      • 我反对把它放在外面,这会降低便携性……跨 Windows 系统。
      【解决方案12】:

      逃离“管道”:

      echo ^| ^< ^> ^& ^\ ^^
      

      【讨论】:

      • 我敢打赌 DOS 转义字符并不为人所知。不错。
      • 啊,这可以解释为什么它也是行继续运算符——它转义换行符,就像 bash 中的 \...
      • @leander:是的,但它只能逃避 LF(而不是 CR),因为之前所有的 CR 都被删除了
      • 所以 this 就是为什么在 Windows 上使用 git 来区分之前的提交 (HEAD^) 是有问题的。
      【解决方案13】:

      能够运行命令并处理输出(如 bash 中的“$()”的反引号)。

      for /f %i in ('dir /on /b *.jpg') do echo --^> %i
      

      如果文件名中有空格,请使用:

      for /f "tokens=*" %i in ('dir /on /b *.jpg') do echo --^> %i
      

      【讨论】:

      • 不适用于名称中包含空格的文件名...这有效: for /f "tokens=*" %i in ('dir /on /b *.jpg') do回声 --^> %i
      • 好收获。我个人认为文件名中的空格是来自地狱第九圈深处的邪恶可怕的东西。但我想我们应该迎合他们。
      • 这是我见过的最疯狂的语法。如果你制作了这个 backtick.bat 并传入字符串,它会起作用吗?
      【解决方案14】:

      创建一个空文件:

      > copy nul filename.ext
      

      【讨论】:

      • @devio:回声。放一个空行。所以文件不会是空的!
      • 我为此使用type nul &gt; filename.ext
      • 很好,我想知道是否有很长一段时间的触摸等效项。
      • 我一直用ren &gt; blah.txt
      【解决方案15】:

      隐藏命令重定向到>nul 2>&1的所有输出。

      例如,某些命令行程序即使重定向到 >nul 也会显示输出。但是,如果你像下面的行那样重定向输出,所有的输出都将被抑制。

      PSKILL NOTEPAD >nul 2>&1
      

      编辑:请参阅 Ignoring the output of a command 了解其工作原理。

      【讨论】:

      • 你知道这是怎么回事吗? 2>&1位是什么意思?
      • >nul 将 STDOUT 重定向到 nul。 2>&1 将 STDERR 重定向到 STDOUT 指向的任何位置。
      • 具体来说,我认为 2>&1 是“文件句柄克隆”,将 STDERR 设置为 STDOUT 的克隆。将它放在 >NUL 之后很重要,因为您希望在 STDOUT 之后 它已被重定向,而不是之前。 (如果我在这里错了,请纠正我。)
      • 对于那些想知道 PSKILL 来自哪里的人,请查看sysinternals.com。如果您使用的是专业版,则应该有一个或多或少相同的原生 TSKILL 命令。
      • 如果你没有 pskill 或 tskill,我用过的大多数 Windows 系统都带有taskkill
      【解决方案16】:
      PAUSE
      

      停止执行并显示如下提示:

      按任意键继续。 . .

      如果您想通过在 Windows 资源管理器中双击来运行批处理并希望实际查看输出而不仅仅是命令窗口的闪烁,这很有用。

      【讨论】:

      • 我几乎不会称其为“未充分利用”,因为我将其标记到我编写的每个脚本的末尾。再说一次,当你想让你的 IDE 运行脚本并捕获输出时,它就不能很好地工作了,因为 IDE 通常无法为你按下回车键......
      • “暂停”的一个简洁功能是,如果周围没有终端可以接收“任何密钥”(例如,如果您的批处理文件是从系统服务运行的),它会检测到这一点并继续运行...
      • +1 给 Charlie Somerville,这是众所周知的,早在 90 年代初,每个游戏程序员的“go.bat”都会使用它。
      • 您可以使用开始/运行/然后键入“cmd /k”和批处理文件名,而不是污染所有批处理文件(并使它们对 CLI 极客来说很烦人)。或将 HKCR\batfile\shell\open\command 默认字符串更改为 'cmd /k "%1" %*'。或者制作另一个只运行'@cmd /k $*'的批处理文件,把它放在桌面上,然后把你的其他批处理文件放在上面。 PAUSE 有很多替代方案。请考虑他们。
      • @gnud:您仍然可以通过管道传输到脚本:echo.|batch-with-pause.bat 将具有与按“任意键”相同的效果...
      【解决方案17】:

      相当于 bash(和其他 shell)

      echo -n Hello # or
      echo Hello\\c
      

      它输出“Hello”而没有尾随换行符。执行此操作的 cmd hack:

      <nul set /p any-variable-name=Hello
      

      set /p 是一种提示用户输入的方式。它发出给定的字符串,然后等待(在同一行,即没有 CRLF),让用户输入响应。

      &lt;nul 只是将空响应通过管道传递给set /p 命令,因此最终结果是发出的提示字符串。 (由于空响应,使用的变量保持不变。)

      问题是:无法输出前导等号,并且在 Vista 上会删除前导空白字符,但在 XP 上不会。

      【讨论】:

        【解决方案18】:

        设置环境变量时搜索替换:

        > @set fname=%date:/=%
        

        ...从日期中删除“/”以用于带时间戳的文件名。

        还有子字符串...

        > @set dayofweek=%fname:~0,3%
        

        【讨论】:

          【解决方案19】:

          整数运算:

          > SET /A result=10/3 + 1
          4
          

          【讨论】:

          • SET 拥有计算能力多久了? Windows XP?
          • 如果您要问值可以有多大,我相信这是 32 位的。所以+/- 20亿。
          • 我认为问题是,SET 能够计算多长时间?从 Windows XP 开始?
          • 我觉得 CMD.EXE 的 SET 从 NT 3.1 左右就可以计算了。只是花了很长时间才注意到 CMD.EXE 与 COMMAND.COM 不完全相同...
          【解决方案20】:

          命令分隔符:

          cls & dir
          copy a b && echo Success
          copy a b || echo Failure
          

          在第 2 行,&& 之后的命令只有在第一个命令成功的情况下才会运行。

          在第 3 行,|| 之后的命令仅在第一个命令失败时运行。

          【讨论】:

            【解决方案21】:

            输出一个空行:

            echo.
            

            【讨论】:

              【解决方案22】:

              您可以链接 if 语句以获得类似短路布尔值“和”的效果。

              if foo if bar baz
              

              【讨论】:

              • 这实际上只是一个嵌套 if 的快捷方式:if foo ( if bar ( baz ) )(想象括号后的换行符。)
              【解决方案23】:

              快速将 Unicode 文本文件(16 位/字符)转换为 ASCII DOS 文件(8 位/字符)。

              C:\> type unicodeencoded.txt > dosencoded.txt
              

              作为奖励,如果可能的话,字符被正确映射。

              【讨论】:

              • 这是一个 ANSI DOS 文件 - ASCII 是 7bit/char。
              • 大声笑,这是一种反模式,这种转换很可能会失败。唯一支持 Unicode 的 8 位编码是 UTF-8,但由于 M$,您无法在 Windows 上设置 UTF-8 代码页。
              【解决方案24】:

              if 块结构:

              if "%VS90COMNTOOLS%"=="" (
                echo: Visual Studio 2008 is not installed
                exit /b
              )
              

              【讨论】:

              • 只要你知道变量将被一次性扩展(没有延迟扩展) - 即你不能在那里明智地使用 %ERRORLEVEL%。
              • 你也不能在那里使用标签(包括那些 :: 样式的 cmets),因为它会过早地结束 if 语句。
              • @Duncan:无论如何你都不应该使用伪变量%ERRORLEVEL%;这就是if errorlevel &lt;foo&gt; 的用途。而 that 实际上确实在这样的块中起作用。
              【解决方案25】:

              变量的延迟扩展(为了很好的措施,加入了子字符串):

                  @echo off
                  setlocal enableextensions enabledelayedexpansion
                  set full=/u01/users/pax
              :loop1
                  if not "!full:~-1!" == "/" (
                      set full2=!full:~-1!!full2!
                      set full=!full:~,-1!
                      goto :loop1
                  )
                  echo !full!
                  endlocal
              

              【讨论】:

                【解决方案26】:

                不提供太多功能,但您可以将 title 命令用于多种用途,例如在任务栏中的长脚本上提供状态,或仅用于增强用户反馈。

                @title Searching for ...
                :: processing search
                @title preparing search results
                :: data processing
                

                【讨论】:

                • 有趣。尽管此后您显然失去了常规功能,即显示当前正在运行的命令。有什么办法可以重置吗?
                • technet.microsoft.com/en-us/library/bb491017.aspx 说它可以用“标题”自行重置,但这似乎不适用于 Windows 7...
                【解决方案27】:

                手头没有编辑器,需要创建批处理文件?

                copy con test.bat
                

                只需键入命令,按回车换行。 按 Ctrl-Z 和 Enter 关闭文件。

                【讨论】:

                  【解决方案28】:

                  datetime 上的字符串减法示例以获取名为“YYYY-MM-DD HH:MM:SS.txt”的文件

                  echo test &gt; "%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%_%time:~3,2%_%time:~6,2%.txt"

                  我使用color 来指示我的脚本是成功、失败还是需要通过更改文本和背景颜色来进行一些输入。当您有一些机器在您的视野范围内但距离很远时,这真的很有帮助

                  颜色 XY

                  其中 X 和 Y 是从 0F 的十六进制值,其中 X - 背景,Y - 文本,当 X = Y 颜色不会改变。

                  颜色 Z

                  将文本颜色更改为“Z”并设置黑色背景,“颜色 0”不起作用

                  颜色名称调用

                  颜色?

                  【讨论】:

                  • 已编辑; _ 被解释为斜体。不错的代码。
                  • @Duncan Smart:不正确,也适用于英国英语(虽然技术上应该是“color”,grrr)
                  【解决方案29】:

                  使用空格和转义字符完全控制输出。:

                  echo.    ^<resourceDir^>/%basedir%/resources^</resourceDir^>
                  

                  【讨论】:

                  • 它是如何工作的?点划定文本输出的开头?
                  • "echo.x" 会输出"x","echo x" 只会输出"x"。这允许前导空格。此外,“^”转义字符将阻止 cmd 认为所有那些“”字符都是 I/O 重定向。
                  • echo( 更好,因为 echo. 创建一个文件搜索名为“echo”的文件,如果该文件存在则 echo. 失败,如果不存在则执行内部 echo,但它比 echo 慢(
                  【解决方案30】:

                  TheSoftwareJedi 已经提到了 for 命令,但我将再次提及它,因为它非常强大。

                  下面以 YYYYMMDD 格式输出当前日期,我在生成备份目录时使用它。

                  for /f "tokens=2-4 delims=/- " %a in ('DATE/T') do echo %c%b%a
                  

                  【讨论】:

                  • 肯定 DATE /T 在欧洲返回 29/10/2008,在美国返回 10/29/2008...所以可能需要进行一些本地化! ;-)
                  • 没错!但是你可以滥用 date 命令来找出使用的日期格式。
                  • 这是过度使用 FOR,imo。我想我只会使用 %DATE:~10,4%%DATE:~4,2%%DATE:~7,2% 而不是运行日期命令然后解析它。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-10-07
                  • 2011-08-16
                  • 1970-01-01
                  • 2011-12-15
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多