为什么不能在批处理循环中使用问号?

时间:2018-08-02 01:01:25

标签: windows batch-file for-loop cmd

前言:

在编写单独的代码时,我在for循环中遇到了问号问题。如下所示,在for循环中不会访问问号。

批处理文件:

@echo off

for %%x in (the, quick, ?, brown, fox) do (
    echo %%x
)

输出:

the
quick
brown
fox

此外,这不适用于CMD(当然使用%x而不是%%x),或者使用""[],{{1} },^\或类似的字符转义方法。

此外,使用计数器变量来确定括号中的代码被访问的次数仅导致总数为4,这意味着%命令显然不是问题。


问题:

  

为什么问号在这样的标准循环中不起作用,我该如何解决?

3 个答案:

答案 0 :(得分:3)

这是因为?将扩展为一个字符长的文件名列表。 “裸” for使用该列表作为文件名列表。

如果运行以下命令,则会看到实际效果:

c:\> echo xx >a
c:\> echo xx >b
c:\> for %i in (1, ?) do echo %x
1
a
b

如果查看Rob van der Woude's excellent scripting pages,您会发现for page具有用于处理命令输出,数字和文件的选项-它实际上并不适合于任意字符串。

一种解决方法是提供您自己的 own for类似的命令,如以下示例所示:

    @echo off
    setlocal enableextensions enabledelayedexpansion

    rem Call the callback function for each argument.
    set escapee=/
    call :doFor :processEach 1 2 ? 4 5
    echo.Escapee was %escapee%

    rem Execute simple command for each argument.
    call :doFor echo 6 7 ? 9 10

    endlocal
    goto :eof

:processEach
    set escapee=%escapee%%1/
    goto :eof

:doFor
    setlocal

    rem Get action.
    set cbAction=%1
    shift

:dfloop
    rem Process each argument with callback or command.
    if not "%1" == "" (
        call %cbAction% %1
        shift
        goto :dfloop
    )
    endlocal&&set escapee=%escapee%
    goto :eof

这提供了一个可以处理回调和简单命令的功能。对于更复杂的命令,请提供一个回调函数,它将随每个参数依次调用。回调函数可以任意复杂,但是请记住,由于它在setlocal中运行,因此对环境变量的更改无法逃逸回调用者。

为此,它允许一个变量escapee逃脱作用域-您也可以根据需要添加更多内容。

对于简单的命令(例如echo),您只需要在其末尾放置参数,即可执行相同的操作。它不需要回调函数,但仅限于非常简单的情况。

还要记住,尽管这看起来像很多代码,但绝大多数代码只需要存在于一个地方即可。要使用它,您只需要像示例一样的单线:

call :doFor echo my hovercraft is full of eels

还请记住,即使使用此方案,也可能会有 other 个字符表现不佳。它解决了?问题,但其他问题仍然可能引起问题。我怀疑这是将PowerShell添加到您的简历中的理想机会,例如,该命令的优雅和禅意几乎与bash类似:

PShell> foreach ($item in @("1", "?", "3", "4")) { echo $item }
1
?
3
4

答案 1 :(得分:1)

您可以切换到FOR /F
但是FOR / F用于处理多行以将其拆分为令牌。
如果您不需要多个令牌,则每个项目需要一个循环。

这可以通过使用换行符拆分项目来实现。
我使用#作为项目分隔符,但您可以自由使用任何其他字符

@echo off
setlocal EnableDelayedExpansion
(set \n=^
%=EMPTY=%
)

set "itemList=the#quick#?#brown#fox"
for %%L in ("!\n!") DO (
    FOR /F "delims=" %%x in ("!itemList:#=%%~L!") DO echo - %%x -
)

输出:

- the -
- quick -
- ? -
- brown -
- fox -

答案 2 :(得分:1)

我已经进行了很多年的编码工作,直到现在我都惊讶地意识到这个问题!

我找到了解决此问题的另一种方法。可能有人喜欢它,例如我。

在我特殊的情况下,我使用FOR LOOP获取当前函数的一些命名参数。这就是我所做的:

:SomeFunct
rem Replace ?
set "args=%*"
set "args=%args:?=`%"

rem Iterate args
for %%p in (%args%) do (
    for /f "tokens=1,* delims=: " %%a in ("%%~p") do (
        rem Get and store values
        if /i "%%~a" equ "/a" set "argA=%%~b"
        if /i "%%~a" equ "/b" set "argB=%%~b"
        if /i "%%~a" equ "/c" set "argC=%%~b"
    )
)

rem Restore ?
if defined argA set "argA=%argA:`=?%"
if defined argB set "argB=%argB:`=?%"
if defined argC set "argC=%argC:`=?%"

rem I use the args
rem ...

rem Return
goto:eof

我这样调用函数:

rem Calling example
call:SomeFunct "/a:Is there" "/b:a question mark" "/c in the arguments?"