如何向批处理文件传递参数?

1419

我需要在运行批处理文件时传递ID和密码,而不是将它们硬编码到文件中。

以下是命令行的样子:

test.cmd admin P@55w0rd > test-log.txt

9
有关“其余所有内容”,请参阅Greg Hegill在如何从第N个位置获取批处理文件参数?的评论。 - matt wilkie
我有一个环境启动脚本,可以将我的用户名/密码推送到环境变量中...这样我就不必每次都输入它们了...虽然我大多数时候都在使用bash(Linux、Mac和Windows),但我需要在工作中为脚本使用它来配置代理等。 - Tracker1
20个回答

1272

另一个有用的提示是使用%*表示“全部”。例如:

echo off
set arg1=%1
set arg2=%2
shift
shift
fake-command /u %arg1% /p %arg2% %*

当你执行以下命令时:
test-command admin password foo bar

上述批处理文件将运行:

fake-command /u admin /p password admin password foo bar

我可能语法稍有错误,但这是一般思路。


148
%* 会将所有参数展开,不受移位影响。因此,在两次移位后,你仍然可以在%*中看到前两个参数。你可以使用类似这样的命令:https://dev59.com/0nRA5IYBdhLWcg3w_C4w#761658 获取一个变量,其中包含除前n个参数以外的所有内容。 - Joey
1
请注意,%*并非在所有情况下都有效!例如,在DOSBox 0.73中无法使用(也许这是应该报告的错误)。 - Denilson Sá Maia
36
在MS-DOS或Win9x中,%*从一开始就没有起作用,所以这不是一个错误。 - Kef Schecter
9
这个答案是错误的。实际上会导致 fake-command /u admin /p password admin password foo bar。这已经在多年前被 @Joey 指出。 - jwdonahue
2
在这种情况下,这两个移位是没有意义的。在它们之后,%1 将变成 foo,%2 将变成 bar,但这些值没有被使用,而 %* 不受移位命令的影响。 - Fotios Basagiannis
显示剩余3条评论

357

这是我做到它的方式:

@fake-command /u %1 /p %2

以下是该命令的样例:

test.cmd admin P@55w0rd > test-log.txt

%1 应用于第一个参数,%2 (这是棘手的部分) 应用于第二个参数。您可以通过这种方式传递最多9个参数。


20
如果你跟我一样蠢,你的脑海里正在寻找 echo %1 %2 ,然而由于有个带有 @ 和参数的 fake-command 的存在,这个最简单的情况不可复制,你会被扰乱了,认为我们之后会得到 fake-command.bat 的内容(在这种情况下,过于复杂的 fake-command.bat 可能会使用 echo %2 %4 来忽略参数名称)。 错了,笨蛋。 TL;DR: 不要像我一样蠢。 1. echo echo %1 %2 > test.bat 2. test word1 word2。3. 获利。 - ruffin
7
五年后,我重新阅读我的评论感到困惑。好像是想要“创建一个带有两个参数的.bat文件,直接输入echo echo %1 %2 > test.bat。test.bat文件中将会有echo %1 %2(你也可以从文本编辑器中保存它)。现在输入test word1 word2以调用并查看参数是否起作用。word1 word2将被回显到命令行。(echo %2 %4将忽略/u/p,所以你可以调用test /u word1 /p word2来获得相同的结果)。@在bat文件中表示该命令不会被重复执行。 - ruffin
1
你可能也需要一些引号处理/保护,例如:SetLocalSet dbName=%1Rem dbName如果未定义dbName,则转到SyntaxErrREM 删除所有(/任何)引号 Set dbName=%dbName:"=%如果"%dbName%" == "",则转到SyntaxErr - DennisVM-D2i

175

如果您想智能处理缺失参数,可以尝试以下方法:

IF %1.==. GOTO No1
IF %2.==. GOTO No2
... do stuff...
GOTO End1

:No1
  ECHO No param 1
GOTO End1
:No2
  ECHO No param 2
GOTO End1

:End1

4
这些等式操作中的点号/句点有什么意义? - Chris
11
基本上,如果%1为空,这将变成IF . ==.,因此将发生GOTO。我们在这里使用x,所以:IF x%1==x->IF x==x-> true。 - Carl
3
你应该问什么是“未提供的参数”的价值?你应该检查哪些方面的“未提供的参数”?如果你没有答案,可以使用类似点的技巧。请记住,正如在 https://ss64.com/nt/if.html 中所述:“实际上你可以使用几乎任何字符,比如'~'、花括号{}甚至数字4,但方括号往往被选择,因为它们没有任何特殊含义。” - Adriano G. V. Esposito

102

通过使用%1,%2...%9或者%*,访问批处理参数可以变得简单,但只适用于内容简单的情况。

对于像"&"^&这样复杂的内容,没有简单的方法,因为无法访问%1而不产生错误。

set  var=%1
set "var=%1"
set  var=%~1
set "var=%~1"

这些行会自动扩展到

set  var="&"&
set "var="&"&"
set  var="&"&
set "var="&"&"

每一行都失败了,因为其中一个&在引号之外。

可以通过读取参数的注释版本的临时文件来解决这个问题。

@echo off
SETLOCAL DisableDelayedExpansion

SETLOCAL
for %%a in (1) do (
    set "prompt="
    echo on
    for %%b in (1) do rem * #%1#
    @echo off
) > param.txt
ENDLOCAL

for /F "delims=" %%L in (param.txt) do (
  set "param1=%%L"
)
SETLOCAL EnableDelayedExpansion
set "param1=!param1:*#=!"
set "param1=!param1:~0,-2!"
echo %%1 is '!param1!'
关键在于启用echo on,并在rem语句之后扩展%1(也适用于%2 .. %*)。这样即使是类似"&"&的字符也可以被回显而不会产生错误,因为已经进行了注释。
但是要能够重定向echo on的输出,需要使用两个for循环。
额外的字符* #用于防止内容出现如 /? (将显示REM的帮助信息)之类的情况。或者,在行末添加一个插入符号^可以作为多行字符,甚至在rem之后也是如此。
然后从文件中读取rem参数的输出,但要小心处理。FOR /F应该使用延迟扩展关闭,否则包含“!”的内容将被破坏。在删除param1中的额外字符之后,您就得到它了。
要安全地使用param1,请启用延迟扩展。

1
@KevinEdwards 然后尝试使用一个,你的解决方案仅适用于简单内容。 - jeb
1
@KevinEdwards 我测试了一下,test.bat ^& 失败了。只有 test.bat "&" 可以正常工作,但这不是我的重点。如果没有 REM 技巧,你无法安全地使用 %*%1 - jeb
啊,是的,插入符号没有正确转义。你需要加引号,这对我遇到的所有用例都可行。我只是想传递一下。感谢你的测试!干杯 :) - Kevin Edwards
我对这个方法(echo-on-and-rem-piped-to-a-file trick)有一些担忧:虽然批处理和cmd.exe不允许在命令参数中使用换行符,但是对于调用脚本时允许使用的shell,这个技巧会失效。不要笑,因为PowerShell允许在参数中使用换行符。使用低级别的CreateProcess()函数也可以实现换行并打破它。 - Explorer09
2
@jwdonahue 是的,这很复杂(但不是过于复杂)。但对于密码来说可能是必要的,至少我的一些密码包含“&”和引号“”。 - jeb
显示剩余5条评论

70
这种技术有点额外的开销,但它使我的批处理文件非常易于理解和快速实施。除此之外,还支持以下结构:
>template.bat [-f] [--flag] [--namedvalue value] arg1 [arg2][arg3][...]

这个意思是有一个:init:parse:main函数。

template.bat

@::!/dos/rocks
@echo off
goto :init

:header
    echo %__NAME% v%__VERSION%
    echo This is a sample batch file template,
    echo providing command-line arguments and flags.
    echo.
    goto :eof

:usage
    echo USAGE:
    echo   %__BAT_NAME% [flags] "required argument" "optional argument" 
    echo.
    echo.  /?, --help           shows this help
    echo.  /v, --version        shows the version
    echo.  /e, --verbose        shows detailed output
    echo.  -f, --flag value     specifies a named parameter value
    goto :eof

:version
    if "%~1"=="full" call :header & goto :eof
    echo %__VERSION%
    goto :eof

:missing_argument
    call :header
    call :usage
    echo.
    echo ****    MISSING "REQUIRED ARGUMENT"    ****
    echo.
    goto :eof

:init
    set "__NAME=%~n0"
    set "__VERSION=1.24"
    set "__YEAR=2023"

    set "__BAT_FILE=%~0"
    set "__BAT_PATH=%~dp0"
    set "__BAT_NAME=%~nx0"

    set "OptHelp="
    set "OptVersion="
    set "OptVerbose="

    set "UnNamedArgument="
    set "UnNamedOptionalArg="
    set "NamedFlag="

:parse
    if "%~1"=="" goto :validate

    if /i "%~1"=="/?"         call :header & call :usage "%~2" & goto :end
    if /i "%~1"=="-?"         call :header & call :usage "%~2" & goto :end
    if /i "%~1"=="--help"     call :header & call :usage "%~2" & goto :end

    if /i "%~1"=="/v"         call :version      & goto :end
    if /i "%~1"=="-v"         call :version      & goto :end
    if /i "%~1"=="--version"  call :version full & goto :end

    if /i "%~1"=="/e"         set "OptVerbose=yes"  & shift & goto :parse
    if /i "%~1"=="-e"         set "OptVerbose=yes"  & shift & goto :parse
    if /i "%~1"=="--verbose"  set "OptVerbose=yes"  & shift & goto :parse

    if /i "%~1"=="--flag"     set "NamedFlag=%~2"   & shift & shift & goto :parse
    if /i "%~1"=="-f"         set "NamedFlag=%~2"   & shift & shift & goto :parse

    if not defined UnNamedArgument     set "UnNamedArgument=%~1"     & shift & goto :parse
    if not defined UnNamedOptionalArg  set "UnNamedOptionalArg=%~1"  & shift & goto :parse

    shift
    goto :parse

:validate
    if not defined UnNamedArgument call :missing_argument & goto :end

:main
    if defined OptVerbose (
        echo **** DEBUG IS ON
    )

    echo UnNamedArgument:    "%UnNamedArgument%"

    if defined UnNamedOptionalArg      echo UnNamedOptionalArg: "%UnNamedOptionalArg%"
    if not defined UnNamedOptionalArg  echo UnNamedOptionalArg: not provided

    if defined NamedFlag               echo NamedFlag:          "%NamedFlag%"
    if not defined NamedFlag           echo NamedFlag:          not provided

:end
    call :cleanup
    exit /B

:cleanup
    REM The cleanup function is only really necessary if you
    REM are _not_ using SETLOCAL.
    set "__NAME="
    set "__VERSION="
    set "__YEAR="

    set "__BAT_FILE="
    set "__BAT_PATH="
    set "__BAT_NAME="

    set "OptHelp="
    set "OptVersion="
    set "OptVerbose="

    set "UnNamedArgument="
    set "UnNamedArgument2="
    set "NamedFlag="

    goto :eof
用例 返回结果
template.bat













模板 v1.24
这是一个示例的批处理文件模板,
提供命令行参数和标志。

用法:
template.bat [flags] "required argument" "optional argument"

/?, --help 显示此帮助
/v, --version 显示版本号
/e, --verbose 显示详细输出
-f, --flag value 指定命名参数值

**** 缺少 "REQUIRED ARGUMENT" ****
template.bat /?











模板 v1.24
这是一个示例的批处理文件模板,
提供命令行参数和标志。

用法:
template.bat [flags] "required argument" "optional argument"

/?, --help 显示此帮助
/v, --version 显示版本号
/e, --verbose 显示详细输出
-f, --flag value 指定命名参数值
template.bat --v 1.24
template.bat --version


模板 v1.24
这是一个示例的批处理文件模板,
提供命令行参数和标志。
template.bat -e arg1



**** 调试已开启
未命名参数:"arg1"
未命名可选参数:未提供
命名标志:未提供
template.bat --flag "my flag" arg1 arg2
(without -e)

未命名参数: "arg1"
未命名可选参数:"arg2"
命名标志:"my flag"
template.bat --verbose "argument #1" --flag "my flag" second


**** 调试已开启
未命名参数:"argument #1"
未命名可选参数:"second"
命名标志:"my flag"

2
真是一件美妙的事情!对于未来的读者,我只想强调一下,-f标志实际上还没有包含在解析部分中。提前告诉你! - Flo
我接受了你的编辑@Flo。谢谢! - kodybrown
1
第一行 @::!/dos/rocks 是做什么用的? - MrJman006
1
这是一行注释,所以它什么也不做。只是为了好玩!Bash脚本有shebang,所以我开始在我的批处理文件中使用它。;)(我知道shebang实际上是指令..) - kodybrown
太棒了!在查找了一个方法后,终于找到了一个单一文件适用于bash和batch的解决方案,正如这个SO帖子所提到的。 - kyrlon
你把所有的内容都涵盖了。非常棒的回答。 - undefined

65

是的,在使用iffor等命令时,不要忘记使用变量,例如%%1

如果忘记了双重百分号%,则可能会替换掉(可能为空的)命令行参数,并收到一些非常混乱的错误消息。


%% 只用于 iffor 语句吗? - Royi Namir
29
比那更糟糕的是,在批处理文件中,%%用于前缀变量和命令行参数的内部。但是,当你从命令行使用这些命令时,你只需要使用%作为前缀。例如:在批处理中:for %%d in (*) do echo %%d 从命令行中: for %d in (*) do echo %d - Steve Midgley
14
@SteveMidgley 我大约一年前点赞了您的评论。然后我马上忘记了它,直到今天我试图在命令行中查看我的for循环并困惑地盯着它,想知道为什么它没有执行任何其他操作。因此,这是我再次送给您的虚拟点赞。当我再次遇到同样的问题时,我将在另一年左右回来。 - icc97
据我所知,我们通常将数字保留用于命令行处理,双百分号仅适用于for语句。在批处理文件中,当以1 (test 1) 调用 @for %%2 in (testing) do @echo %%2 %1 时,会生成 testing 1 - jwdonahue
2
答案是错误的。 %%1不能访问命令行参数,对于IF命令也是错误的。只有在批处理文件中使用FOR命令需要双百分号,但即使如此,%%1也只能定义一个FOR参数,而不能访问参数。 - jeb

56

不需要把它复杂化。它只是命令 %1 %2 参数,例如:

@echo off

xcopy %1 %2 /D /E /C /Q /H /R /K /Y /Z

echo copied %1 to %2

pause

“暂停”命令会显示批处理文件执行的内容,并等待您按下任意键。将其保存为xx.bat文件并放在Windows文件夹中。

要使用它,例如输入:

xx c:\f\30\*.* f:\sites\30

这个批处理文件会处理所有必要的参数,例如仅复制那些更新的文件等等。我从 Windows 以前就一直在使用它。如果你希望看到正在进行复制的文件名,请省略 Q 参数。


40

所有人都回答了非常复杂的问题,但实际上这很简单。 %1 %2 %3 等等是传递给文件的参数。 %1 是第一个参数,%2 是第二个参数,依此类推。

所以,如果我有一个包含以下内容的批处理脚本:

@echo off
echo %1

当我运行批处理脚本时,我输入了这个:

C:> script.bat Hello

脚本将简单地输出这个:

Hello

这对于脚本中某些变量(例如名称和年龄)非常有用。因此,如果我有一个像这样的脚本:

@echo off
echo Your name is: %1
echo Your age is: %2

当我输入这个时:

C:> script.bat Oliver 1000

我得到了这个的输出:

Your name is: Oliver
Your age is: 1000

3
很棒的答案。简明易懂。 - NTDLS
如何处理包含空格的字符串变量? - MeSo2

38

在批处理文件中

set argument1=%1
set argument2=%2
echo %argument1%
echo %argument2%

%1%2分别返回第一个和第二个参数的值。

在命令行中,传递参数。

Directory> batchFileName admin P@55w0rd 

输出结果将会是

admin
P@55w0rd

33
@ECHO OFF
:Loop
IF "%1"=="" GOTO Continue
SHIFT
GOTO Loop
:Continue

注意: 如果%1本身被引号包含,"%1"==""会导致问题。

在这种情况下,请使用IF [%1]==[]或者仅适用于NT 4(SP6)及其后续版本的IF "%~1"==""


足够正确,但它几乎没有触及主题。 - jwdonahue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接