有没有办法在批处理文件中指出最后n个参数?

时间:2009-04-17 18:23:58

标签: parameters shell cmd

在以下示例中,我想从父批处理文件中调用子批处理文件,并将所有剩余参数传递给子代。

C:\> parent.cmd child1 foo bar
C:\> parent.cmd child2 baz zoop
C:\> parent.cmd child3 a b c d e f g h i j k l m n o p q r s t u v w x y z

在parent.cmd中,我需要从参数列表中删除%1,并仅将剩余参数传递给子脚本。

set CMD=%1
%CMD% <WHAT DO I PUT HERE>

我调查过使用SHIFT和%*,但这不起作用。当SHIFT将位置参数向下移动1时,%*仍然指向原始参数。

有人有什么想法吗?我应该放弃并安装Linux吗?

8 个答案:

答案 0 :(得分:70)

遗憾的是,

%*将始终扩展到所有原始参数。但是您可以使用以下代码片段来构建包含除第一个参数之外的所有参数的变量:

rem throw the first parameter away
shift
set params=%1
:loop
shift
if [%1]==[] goto afterloop
set params=%params% %1
goto loop
:afterloop

我认为它可以做得更短,但是......我不经常写这些东西:)

应该可以工作。

答案 1 :(得分:15)

这是使用“for”命令的单行方法......

for /f "usebackq tokens=1*" %%i in (`echo %*`) DO @ set params=%%j

该命令将第一个参数分配给“i”,其余的(用'*'表示)分配给“j”,然后用它来设置“params”变量。

答案 2 :(得分:10)

警告!如果参数中有«*»s或«?»,此方法会产生扩展通配符的副作用。如果存在通配符但没有相应的文件,则跳过参数(非通配符参数保持原样)。如果参数必须保持不变,那么寻找另一种方式。


%CMD% <WHAT DO I PUT HERE>

应改为:

(
  SETLOCAL ENABLEDELAYEDEXPANSION
  SET skip=1

  FOR %%I IN (%*) DO IF !skip! LEQ 0 (
        SET "params=!params! %%I"
    ) ELSE SET /A skip-=1
)
(
  ENDLOCAL
  SET "params=%params%"
)
%CMD% %params%

当然,您可以将 skip var设置为任意数量的参数。

Explaned:

(
@rem Starting block here, because it's read once and executed as one
@rem otherwise cmd.exe reads file line by line, which is waaay slower.

SETLOCAL ENABLEDELAYEDEXPANSION
SET skip=1

@rem if value contains unquoted non-paired parenthesis, SET varname=value 
@rem confuses cmd.exe. SET "a=value" works better even if value has quotes.
  FOR %%I IN (%*) DO (
    IF !skip! LEQ 0 (
      SET "params=!params! %%I"
      @rem newline after SET to lower amount of pitfalls when arguments 
      @rem have unpaired quotes
    ) ELSE (
      SET /A skip-=1
    )
)
(
@rem get variables out of SETLOCAL block
@rem as whole block in parenthesis is read and expanded before executing,
@rem SET after ENDLOCAL in same block will set var to what it was before
@rem ENDLOCAL. All other envvars will be reset to state before SETLOCAL.
ENDLOCAL
SET "params=%params%"
)
@rem doing this outside of parenthesis block to avoid
@rem cmd.exe confusion if params contain unquoted closing round bracket
%CMD% %params%

答案 3 :(得分:7)

你实际上可以这样做:

%*

如果这是该行中的唯一内容,则扩展为使第一个参数 执行命令,并将所有其他参数传递给它。 :)

答案 4 :(得分:1)

另一种方式(几乎与Alxander Bird的相同)没有在子shell中执行ECHO:

FOR /F "usebackq tokens=1*" %%I IN ('%*') DO SET params=%%J

所以,行

%CMD% <WHAT DO I PUT HERE>

看起来像:

FOR /F "usebackq tokens=1*" %%I IN ('%*') DO %CMD% %%J

问题是,如果参数包含带有空格的带引号的stings,cmd.exe将适当地解析它们以使用带编号的参数(%1),但FOR将忽略引号。这个特定的情况,如果第一个参数包含一个或多个空格会很有害,这很可能,考虑到第一个参数可以是,例如,“C:\ Program Files \ Internet Explorer \ iexplore.exe”。

所以,这将是另一个答案。

答案 5 :(得分:1)

我改进了Moshe Goren的答案,因为它似乎对我不起作用:

set _input=%*
call set params=%%_input:%1 =%%
@echo %params%

答案 6 :(得分:0)

虽然在很多情况下确实'for'解决方案是优越的,但对于简单的事情,我会经常保存并转移其他参数,然后像往常一样使用%*(实际上相同的策略通常适用于$*中的$@{,ba,da,k,*}sh}:

<强> 例如:

:: run_and_time_me.cmd - run a command with the arguments passed, while also piping
::                       the output through a second passed-in executable

@echo off

set run_me=%1
set pipe_to_me=%2
shift
shift

:: or
:: set run_me=%1
:: shift
:: set pipe_to_me=%1
:: shift

%run_me% %* | %pipe_to_me%

无论如何,我看到这个问题已经得到了很长时间的回答,但我认为我会贬低我的两分钱,因为这是我没看到的东西,而且因为这是我需要的答案,当我终于碰到它几年时回来......然后去了“哦......呃。” :)

答案 7 :(得分:0)

当我遇到类似的问题时,我遇到了这个问题,但我用不同的方法解决了这个问题。 我没有使用循环重新创建输入行(如@Joey的回答),而是从输入字符串中删除了第一个参数。

set _input=%*
set params=!_input:%1 =!
call OTHER_BATCH_FILE.cmd %params%

当然,这假设所有参数都不同。