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

1419

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

以下是命令行的样子:

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

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

26

让我们保持简单。

这是 .cmd 文件。

@echo off
rem this file is named echo_3params.cmd
echo %1
echo %2
echo %3
set v1=%1
set v2=%2
set v3=%3
echo v1 equals %v1%
echo v2 equals %v2%
echo v3 equals %v3%

这里有三个来自命令行的调用。

C:\Users\joeco>echo_3params 1abc 2 def  3 ghi
1abc
2
def
v1 equals 1abc
v2 equals 2
v3 equals def

C:\Users\joeco>echo_3params 1abc "2 def"  "3 ghi"
1abc
"2 def"
"3 ghi"
v1 equals 1abc
v2 equals "2 def"
v3 equals "3 ghi"

C:\Users\joeco>echo_3params 1abc '2 def'  "3 ghi"
1abc
'2
def'
v1 equals 1abc
v2 equals '2
v3 equals def'

C:\Users\joeco>

1
我知道,很久以前了。但还是谢谢!简单才是最好的,我们可以解决其他的问题。哎,这不是诗吗。 - Alan
1
简单最好,我们可以解决其余的问题!值得铭记。谢谢,艾伦。 - Love and peace - Joe Codeswell

22
FOR %%A IN (%*) DO (
    REM Now your batch file handles %%A instead of %1
    REM No need to use SHIFT anymore.
    ECHO %%A
)
这段代码循环遍历批处理参数(%*),无论它们是否带引号,然后输出每个参数。

这段代码循环遍历批处理参数(%*),无论它们是否带引号,然后输出每个参数。


但是对许多不同的参数都会失败,比如 test.bat *.txttest.bat cat^&dog 或者 Test.bat /? - jeb

21

我写了一个简单的read_params脚本,可以作为函数(或外部.bat)调用,并将所有变量放入当前环境中。它不会修改原始参数,因为函数是使用原始参数的副本进行call调用的。

例如,给定以下命令:

myscript.bat some -random=43 extra -greeting="hello world" fluff

myscript.bat 在调用函数后可以使用这些变量:

call :read_params %*

echo %random%
echo %greeting%

这是函数:

:read_params
if not %1/==/ (
    if not "%__var%"=="" (
        if not "%__var:~0,1%"=="-" (
            endlocal
            goto read_params
        )
        endlocal & set %__var:~1%=%~1
    ) else (
        setlocal & set __var=%~1
    )
    shift
    goto read_params
)
exit /B

限制

  • 不能加载没有值的参数,例如 -force。您可以使用 -force=true,但我无法想到一种方法来允许空值,除非提前知道不会有值的参数列表。

更新日志

  • 2016年2月18日
    • 不再需要延迟扩展
    • 现在可以通过查找参数之前的 - 来使用其他命令行参数。

一直在研究使用这种方法,因为我想以这种方式将参数传递到批处理文件中。但是我注意到,在设置变量之后,即使退出批处理文件,如果访问cmd,则参数仍然设置在其中,并且在批处理结束后,它们不会恢复到先前的状态。这种方法应该如何处理这种情况? - Jono_2007
在这个主题上,这个回答比这个帖子上的一些其他回答更好。 - jwdonahue
扩展以支持标志参数(无值)。抱歉评论中没有格式,但它不起作用。:read_params 如果不是%1/==/ ( 如果不是"%__var%"=="" ( 如果不是"%__var:0,1%"=="-" ( endlocal 转到read_params ) endlocal & set %__var:1%=%1 ) else ( setlocal & set __var=%1 ) shift 转到read_params ) else ( 如果不是"%__var%"=="" ( endlocal & set %__var:~1%=1 ) ) - Max

13

受@Jon在其他地方的答案启发,我制作了一个更通用的算法来提取命名参数、可选值和开关。

假设我们想要实现一个实用程序foobar。它需要一个初始命令。它有一个可选参数--foo,它采用一个可选值(当然不能是另一个参数);如果该值缺失,则默认为default。它还有一个可选参数--bar,它采用一个必需值。最后,它可以采用一个标志--baz,不允许任何值。哦,这些参数可以按任何顺序出现。

换句话说,它看起来像这样:

foobar <command> [--foo [<fooval>]] [--bar <barval>] [--baz]

这里是一个解决方案:

@ECHO OFF
SETLOCAL
REM FooBar parameter demo
REM By Garret Wilson

SET CMD=%~1

IF "%CMD%" == "" (
  GOTO usage
)
SET FOO=
SET DEFAULT_FOO=default
SET BAR=
SET BAZ=

SHIFT
:args
SET PARAM=%~1
SET ARG=%~2
IF "%PARAM%" == "--foo" (
  SHIFT
  IF NOT "%ARG%" == "" (
    IF NOT "%ARG:~0,2%" == "--" (
      SET FOO=%ARG%
      SHIFT
    ) ELSE (
      SET FOO=%DEFAULT_FOO%
    )
  ) ELSE (
    SET FOO=%DEFAULT_FOO%
  )
) ELSE IF "%PARAM%" == "--bar" (
  SHIFT
  IF NOT "%ARG%" == "" (
    SET BAR=%ARG%
    SHIFT
  ) ELSE (
    ECHO Missing bar value. 1>&2
    ECHO:
    GOTO usage
  )
) ELSE IF "%PARAM%" == "--baz" (
  SHIFT
  SET BAZ=true
) ELSE IF "%PARAM%" == "" (
  GOTO endargs
) ELSE (
  ECHO Unrecognized option %1. 1>&2
  ECHO:
  GOTO usage
)
GOTO args
:endargs

ECHO Command: %CMD%
IF NOT "%FOO%" == "" (
  ECHO Foo: %FOO%
)
IF NOT "%BAR%" == "" (
  ECHO Bar: %BAR%
)
IF "%BAZ%" == "true" (
  ECHO Baz
)

REM TODO do something with FOO, BAR, and/or BAZ
GOTO :eof

:usage
ECHO FooBar
ECHO Usage: foobar ^<command^> [--foo [^<fooval^>]] [--bar ^<barval^>] [--baz]
EXIT /B 1
  • 使用SETLOCAL让变量不会逃逸到调用环境。
  • 别忘了初始化变量,如SET FOO=等,以防某人在调用环境中定义了它们。
  • 使用%~1去除引号。
  • 使用IF "%ARG%" == ""而不是IF [%ARG%] == [],因为以空格结尾的值与[]根本不兼容。
  • 即使在IF块内部进行SHIFT,当前的参数(如%~1)也不会更新,因为它们是在解析IF时确定的。你可以在IF块内部使用%~1%~2,但这会令人困惑,因为你有一个SHIFT。你可以在块的末尾放置SHIFT以获得清晰度,但这也可能会被遗漏并/或混淆人们。因此,“捕获”%~1%~1在块外似乎是最好的选择。
  • 你不想在另一个参数的可选值位置使用参数,因此你必须检查IF NOT "%ARG:~0,2%" == "--"
  • 仅在使用其中一个参数时才要小心移位。
  • 重复的代码SET FOO=%DEFAULT_FOO%是遗憾的,但另一种选择是在IF NOT "%ARG%" == ""块之外添加一个IF "%FOO%" == "" SET FOO=%DEFAULT_FOO%。然而,由于这仍然在IF "%PARAM%" == "--foo"块内部,所以%FOO%值将在进入块之前被评估和设置,因此你永远不会检测到既出现了--foo参数,同时%FOO%的值也丢失了。
  • 请注意,ECHO Missing bar value. 1>&2将错误消息发送到stderr。
  • 想在Windows批处理文件中插入空白行?只能使用ECHO:或其变体。

10

要在命令行中引用设置的变量,您需要使用%a%,例如:

set a=100 
echo %a%  
rem output = 100 

注意:此方法仅适用于Windows 7专业版。


7

enter image description here

使用循环获取所有参数并在纯批处理中使用:
注意:不使用以下字符:?* &|<>
@echo off && setlocal EnableDelayedExpansion

 for %%Z in (%*)do set "_arg_=%%Z" && set/a "_cnt+=1+0" && (
     call set "_arg_[!_cnt!]=!_arg_!" && for /l %%l in (!_cnt! 1 !_cnt!
     )do echo/ The argument n:%%l is: !_arg_[%%l]!
 )

endlocal 

你的代码已经准备好在需要的地方处理参数号码,例如...
 @echo off && setlocal EnableDelayedExpansion

 for %%Z in (%*)do set "_arg_=%%Z" && set/a "_cnt+=1+0" && call set "_arg_[!_cnt!]=!_arg_!"
 
 fake-command /u !_arg_[1]! /p !_arg_[2]! > test-log.txt
 

4
简单的解决方案(即使问题很旧)
Test1.bat
echo off
echo "Batch started"
set arg1=%1
echo "arg1 is %arg1%"
echo on
pause

CallTest1.bat

call "C:\Temp\Test1.bat" pass123

输出

YourLocalPath>call "C:\Temp\test.bat" pass123

YourLocalPath>echo off
"Batch started"
"arg1 is pass123"

YourLocalPath>pause
Press any key to continue . . .

在此处,YourLocalPath代表当前目录路径。

为了简单起见,将命令参数存储在变量中,并使用变量进行比较。

这不仅易于编写,而且易于维护。因此,如果以后其他人或您在长时间之后阅读脚本,则很容易理解和维护。

要编写内联代码:请参见其他答案。


3
创建一个新的批处理文件(例如:openclass.bat),并在文件中写入以下内容:
java %~n1

将批处理文件放在system32文件夹中,打开Java类文件,右键点击属性,选择“打开方式”,然后找到批处理文件,选中即可完成...

这对我很有效。

注:我找不到一种方法来在关闭Java类时关闭cmd窗口。暂时还没有解决方案...


在批处理脚本中,使用以下命令可以在进程关闭后关闭cmd窗口: start /wait java %~n1 - Paint_Ninja
1
告诫公众将随机脚本文件放入system32目录是不负责任的行为。这个答案实际上并没有涉及到这个主题的深层次内容。 - jwdonahue

2

成对的参数

如果您更喜欢使用键值对来传递参数,可以使用类似以下的代码:

@echo off

setlocal enableDelayedExpansion

:::::  asigning arguments as a key-value pairs:::::::::::::
set counter=0
for %%# in (%*) do (    
    set /a counter=counter+1
    set /a even=counter%%2
    
    if !even! == 0 (
        echo setting !prev! to %%#
        set "!prev!=%%~#"
    )
    set "prev=%%~#"
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:: showing the assignments
echo %one% %two% %three% %four% %five%

endlocal

一个例子:
c:>argumentsDemo.bat one 1 "two" 2 three 3 four 4 "five" 5
1 2 3 4 5

预定义变量

您还可以预先设置某些环境变量。这可以通过在控制台中设置它们或从我的计算机设置它们来完成:

@echo off

if defined variable1 (
    echo %variable1%
)

if defined variable2 (
    echo %variable2%
)

将其翻译为中文:

并像这样调用:

c:\>set variable1=1

c:\>set variable2=2

c:\>argumentsTest.bat
1
2

列出数值的文件

您也可以指向一个预设所需数值的文件。 如果这是脚本:

@echo off

setlocal
::::::::::
set "VALUES_FILE=E:\scripts\values.txt"
:::::::::::


for /f "usebackq eol=: tokens=* delims=" %%# in ("%VALUES_FILE%") do set "%%#"

echo %key1% %key2% %some_other_key%

endlocal

这是一个 values 文件:
:::: use EOL=: in the FOR loop to use it as a comment

key1=value1

key2=value2

:::: do not left spaces arround the =
:::: or at the begining of the line

some_other_key=something else

and_one_more=more

调用它的输出将是:
"value1 value2 something else"
当然,您可以结合所有方法。还要检查参数语法shift

0

如果您担心安全/密码盗窃(这导致您设计了此解决方案,在执行时获取登录凭据而无需数据库的静态硬编码),那么您可以将密码解密或解密密钥的API或代码的一半存储在程序文件中,因此在运行时,用户将在控制台中键入用户名/密码以进行哈希/解密,然后再通过set /p传递给程序代码以执行,如果您正在查看用户在运行时输入凭据。

如果您正在运行脚本以使用各种用户/密码运行程序,则命令行参数适合您。

如果您正在创建测试文件以查看不同登录的输出/效果,则可以将所有登录信息存储在加密文件中,作为参数传递给test.cmd,除非您想坐在命令行上并键入所有登录信息直到完成。

可以提供的参数数量受命令行上的总字符数限制。为了克服这个限制,前面的段落技巧是一个解决方法,而不会冒险暴露用户密码。


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