web-dev-qa-db-ja.com

バッチファイルの最後のn個のパラメーターを示す方法はありますか?

次の例では、親バッチファイルから子バッチファイルを呼び出し、残りのすべてのパラメーターを子に渡します。

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をあきらめてインストールするだけですか?

43
Dave

%*悲しいことに、常にすべての元のパラメータに展開されます。ただし、次のコードスニペットを使用して、最初のパラメータ以外のすべてを含む変数を作成できます。

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

私はそれはもっと短くできると思いますが...私はこのようなことをあまり頻繁に書きません:)

しかし、うまくいくはずです。

72
Joey

「for」コマンドを使用した1行のアプローチを次に示します...

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

このコマンドは、最初のパラメーターを「i」に割り当て、残り(「*」で示される)は「j」に割り当てられます。これは、「params」変数の設定に使用されます。

15
max_diff

警告!引数に"*"または"?"が含まれている場合、このメソッドにはワイルドカードを展開する副作用があります。ワイルドカードはあるが対応するファイルがない場合、引数はスキップされます(ワイルドカード以外の引数はそのままです)。引数をそのままにする必要がある場合は、別の方法を探します。


この線

%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%

もちろん、skipvarを任意の数の引数に設定できます。

説明:

(
@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%
10
LogicDaemon

あなたは実際にこれを行うことができます:

%*

それが行の唯一のものである場合、最初のパラメーターbeがコマンドに実行され、他のすべてのパラメーターがそれに渡されます。 :)

7
Alexander Bird

Moshe Gorenの答えはうまくいかなかったので改善しました。

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

サブシェルでECHOを実行しない別の方法(Alxander Birdとほぼ同じ):

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

問題は、パラメーターにスペースが含まれる引用符付きの文字列が含まれている場合、cmd.exeはそれらを番号付き引数(%1)として使用するために適切に解析しますが、FORは引用符を無視します。この特定のケースでは、最初の引数にスペースなどが含まれていると問題が発生します。これは、最初の引数が "C:\ Program Files\Internet Explorer\iexplore.exe"である場合を考えると、かなり可能です。

だから、ここに別の答えがあります。

1
LogicDaemon

実際には「for」ソリューションの方が多くの状況で優れていますが、単純なものの場合、他の引数を保存してシフトすることが多いので、通常どおり%*を使用します(実際には、同じ戦略が$*または$@ in {,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%

とにかく、質問が長い間答えられたのを見ましたが、それが私が見なかったものであり、それが私が最後にそれを数年前に遭遇したときに必要だった答えだったので、2セント下がると思っていました。 。そして、「ああ...ああ...」 :)

0

同様の問題に直面しているときにこの質問に出くわしましたが、別の方法で解決しました。ループを使用して入力行を再作成する代わりに(@Joeyによる回答のように)、入力文字列から最初のパラメーターを単に削除しました。

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

もちろん、これはすべてのパラメーターが異なることを前提としています。

0
Moshe Goren