如何从第N个位置获取批处理文件参数?

21

如何在批处理文件中传递命令行参数的基础上,如果我不想指定具体参数,怎么获取其他参数?我不想使用SHIFT,因为我不知道可能会有多少个参数,如果可能的话,我想避免计数。

例如,给定这个批处理文件:

@echo off
set par1=%1
set par2=%2
set par3=%3
set therest=%???
echo the script is %0
echo Parameter 1 is %par1%
echo Parameter 2 is %par2%
echo Parameter 3 is %par3%
echo and the rest are %therest%

执行mybatch opt1 opt2 opt3 opt4 opt5 ...opt20将产生以下结果:

the script is mybatch
Parameter 1 is opt1
Parameter 2 is opt2
Parameter 3 is opt3
and the rest are opt4 opt5 ...opt20

我知道 %* 可以获取所有参数,但我不想要前三个(例如)。


可能是重复的问题:有没有办法在批处理文件中指示最后n个参数? - matt wilkie
5个回答

27

以下是不使用 SHIFT 的方法:

@echo off

for /f "tokens=1-3*" %%a in ("%*") do (
    set par1=%%a
    set par2=%%b
    set par3=%%c
    set therest=%%d
)

echo the script is %0
echo Parameter 1 is %par1%
echo Parameter 2 is %par2%
echo Parameter 3 is %par3%
echo and the rest are %therest%

1
这可能无法正确处理用引号括起来的参数。 - Joey
1
约翰尼斯在这个问题上留下了一个很好的答案:https://dev59.com/0nRA5IYBdhLWcg3w_C4w - Dave
1
@Dave,这个答案有什么不太可靠的地方吗? - Patrick Cuff
@PatrickCuff 如果您运行带有引号参数且其中包含空格的批处理文件,则输出将错误。例如,请尝试这6个参数:"param 1" "param 2" "param 3" 4 5 6。 - dave-holm
两者都存在多个问题。尝试像 --path=c:\a\b\c 这样转发参数。请参阅我的答案,它可以更好地处理这些情况。 - Pavel P

5
@echo off
setlocal enabledelayedexpansion

set therest=;;;;;%*
set therest=!therest:;;;;;%1 %2 %3 =!

echo the script is %0
echo Parameter 1 is %1
echo Parameter 2 is %2
echo Parameter 3 is %3
echo and the rest are: %therest%

只要前三个参数没有特殊的分隔符字符,这可以与带引号的参数和带有等号或逗号的参数一起使用。

示例输出:

test_args.bat "1 1 1" 2 3 --a=b "x y z"
Parameter 1 is "1 1 1"
Parameter 2 is 2
Parameter 3 is 3
and the rest are: --a=b "x y z"

这是通过在原命令行%*中替换%1 %2 %3实现的。前五个分号仅用于确保仅替换第一次出现的这些%1 %2 %3

如果要跳过的参数之间有超过1个空格(即在此示例中的%1 %2 %3),这也可能失败(因为字符串替换将无法工作)。换句话说,为了使其按预期工作,批处理应该正确调用,参数之间只有一个空格。此外,前3个参数不应包含等号。 - OzgurH

1
@ECHO OFF
SET REST=
::# Guess you want 3rd and on.
CALL :SUBPUSH 3 %*
::# ':~1' here is merely to drop leading space.
ECHO REST=%REST:~1%
GOTO :EOF

:SUBPUSH
SET /A LAST=%1-1
SHIFT
::# Let's throw the first two away.
FOR /L %%z in (1,1,%LAST%) do (
  SHIFT
)
:aloop
SET PAR=%~1
IF "x%PAR%" == "x" (
  GOTO :EOF
)
ECHO PAR=%PAR%
SET REST=%REST% "%PAR%"
SHIFT
GOTO aloop
GOTO :EOF

我喜欢使用子程序代替EnableDelayedExpansion。以上是从我的目录/文件模式处理批处理中提取的内容。不要说这不能处理带有=的参数,但至少可以处理带有空格和通配符的引用路径。


1
以下代码使用shift,但它避免了使用for解析命令行,并让命令行解释器完成这项工作(请注意,for不能正确解析双引号,例如参数集A B" "Cfor解释为3个参数AB""C,但由解释器解释为2个参数AB" "C;这种行为会导致像"C:\Program Files\"这样带引号的路径参数无法正确处理):
@echo off

set "par1=%1" & shift /1
set "par2=%1" & shift /1
set "par3=%1" & shift /1

set therest=
set delim=

:REPEAT
if "%1"=="" goto :UNTIL
set "therest=%therest%%delim%%1"
set "delim= "
shift /1
goto :REPEAT
:UNTIL

echo the script is "%0"
echo Parameter 1 is "%par1%"
echo Parameter 2 is "%par2%"
echo Parameter 3 is "%par3%"
echo and the rest are "%therest%"
rem.the additional double-quotes in the above echoes^
    are intended to visualise potential whitespaces
< p > 在 < code > %therest% 中的剩余参数可能与原始参数不同,因为分隔符被替换为单个空格。请记住,命令行解释器也将制表符、逗号、分号和等号作为分隔符,以及所有组合。但是,当将 < code > %therest% 传递给其他命令或批处理文件时,它将被正确解析。

到目前为止,我遇到的唯一限制适用于包含脱字符 < code > ^ 的参数。其他限制(涉及 < code > < 、< code > > 、< code > | 、< code > & 和 < code > " )适用于命令行解释器本身。


+1 是因为它适用于我手头的东西,并且不涉及我所谓的批处理巫术。尽管比其他答案更冗长,但它仍然更难以理解其工作原理。您能否详细说明在哪些暗示的情况下,它将比 FOR 处理得更好? - matt wilkie
1
谢谢!FOR实现只使用空格和制表符作为分隔符,而命令行解释器还允许使用, ; =(可能还有更多?)。您应该将部分"tokens=1-3*"替换为"tokens=1-3* delims=,;=_ "以反映这一点(_代表TAB)。无论如何,解释器不会将双引号分隔符字符(例如," ",",")视为分隔符,但是FOR会;因此参数A B" "C由解释器解释为2个参数AB" "C,而由FOR实现解释为3个参数AB""C - aschipfl
感谢您的解释。我最终仔细阅读了与近似副本有关的链接https://dev59.com/0nRA5IYBdhLWcg3w_C4w并且发现您的答案是@Joey在那里的扩展版本。读者们也应该阅读其他答案; 还有另一种使用“for”的方法可能很有用。 - matt wilkie
我在命令行解释器和 FOR 变量之间遇到了另一个行为差异:如果这些参数包含相邻的两个插入符号 ^,则后者会吸收两倍于前者的数量。当使用解释器方法时,您需要至少2个 ^^ 才能在输出中得到 ^,需要 ^^^^ 才能得到 ^^ 等等;而对于 FOR,您需要至少 ^^^^ 才能得到 ^,等等。 - aschipfl

1

还有一种替代方案 :-)

其他答案中有些没有处理引号或参数之间的额外空格。免责声明:我没有进行过严格的测试。

这只是一个解决方法,因为在shift%*没有更新。太恶心了!

我实现它作为从 @Pavel-P 技术开始的POP。

  • 依靠批处理器来删除多余的空格
  • 它依赖于全局变量 "Params"
  • ~ 移除引号 -- 留给你根据需要重新引用。如果不需要,请删除 ~
:PopFirstParam
    @set varname=%~1
    @set %varname%=%~2
    @rem @echo %varname% = '!%varname%!'
    @if '%3' equ '' set Params=&goto :eof
    @call :popone %Params%
@goto :eof
:popone
    @set Params=;;;%*
    @set Params=!Params:;;;%1 =!
@goto :eof

使用方法:

@setlocal enabledelayedexpansion
@set Params=%*
@call :PopFirstParam P1 %Params%
@call :PopFirstParam P2 %Params%
@call :PopFirstParam P3 %Params%
@call :PopFirstParam P4 %Params%
@rem etc . . .

具体来说,我使用它可选择异步运行命令:

@setlocal enabledelayedexpansion
@rem set variables that decide what to be run  . . . . 

@call :RunIfDefined doNum1 "Title 1"      mycmd1.exe     some  "params"   here
@call :RunIfDefined doNum2 "MyTitle 2"    mylongcmd2.exe some  "params"   here
@call :RunIfDefined doNum3 "Long Title 3" mycmd3.exe     some  "params"   here
@goto :eof

:RunIfDefined
    @set Params=%*
    @call :PopFirstParam DefineCheck %Params%
    @if not defined %DefineCheck% goto :eof
    
    @call :PopFirstParam WindowTitle %Params%
    @call :PopFirstParam Command %Params%
    @rem %Params% should now be the remaining params (I don't care about spaces here)
    @echo start "%WindowTitle%" "%Command%" %Params%
    @start "%WindowTitle%" "%Command%" %Params%
@goto :eof

C:\test> set doNum1=yes
C:\test> set doNum3=yes
C:\test> call dothings.bat

start "Title 1" "mycmd1.exe"     some  "params"   here
start "Long Title 3" "mycmd3.exe"     some  "params"   here

太糟糕了,DOS 没有 #include。


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