バッチファイル-コマンドライン引数の数


99

いくつかのシェルスクリプトをバッチファイルに変換するだけで、見つからないように見えることがあります...これは、コマンドライン引数の数の単純なカウントです。

例えば。あなたが持っている場合:

myapp foo bar

シェル:

  • $#-> 2
  • $ *-> foo bar
  • $ 0-> myapp
  • $ 1-> foo
  • $ 2->バー

バッチで

  • ?? -> 2 <----どのコマンド?!
  • %*-> foo bar
  • %0-> myapp
  • %1-> foo
  • %2->バー

それで私は周りを見回しましたが、間違った場所を探しているか、盲目ですが、渡されたコマンドライン引数の数をカウントする方法を見つけることができないようです。

バッチファイル用のシェルの「$#」に似たコマンドはありますか?

ps。私が見つけた最も近いのは、%1sを反復処理して 'shift'を使用することですが、後でスクリプトで%1、%2などを参照する必要があるので、これは良くありません。


あなたの文字列は2 myapp foo bar
PsychoData 2014

2
アドバイス:shをBATに変換しないでください。代わりに、cygwinをダウンロードして代わりに使用してください。つまり、shスクリプトはWindowsマシンで動作します。すべてのshをBATに変換する必要はありません!
Marty McGowan

回答:


104

少しグーグルすると、wikibooksから次の結果が得られます。

set argC=0
for %%x in (%*) do Set /A argC+=1

echo %argC%

cmd.exeは古いDOS時代から少し進化したようです:)


7
このバリアントはfor、ファイル名のように見える引数でのみ機能し、などのオプション文字列では機能しないことに注意してください-?。引用符(for %%i in ("%*") ...)を使用すると、引数と同様に機能します-?が、引用符がネストされているため、引用符付きの引数では失敗します。唯一の強固な方法には、次のものが含まれるようshiftです...
フェルディナンドベイヤー2013

1
MyBatchFile "*.*" "Abc"argC == 9であると報告します。-反対投票。
BrainSlugs83 2018

これを関数として2500個の引数までテストし、同じサイズの配列を定義するために使用しました。正確に理由を聞かないでください。ほとんどの場合、バッチで実行できる機能を学習するだけです。
T3RR0R

44

次のようなロジックで引数の数を処理する傾向があります。

IF "%1"=="" GOTO HAVE_0
IF "%2"=="" GOTO HAVE_1
IF "%3"=="" GOTO HAVE_2

引数が9個を超える場合は、このアプローチに失敗します。ここで見つけることができるカウンターを作成するためのさまざまなハックがありますが、これらは気の弱い人のためではないことに注意してください。


6
を使用shiftして、9行以上をカウントすることができます。また、10行の同等に見えるコードがなくても使用できます。
ジョーイ

19

:getargc以下の関数はあなたが探しているものかもしれません。

@echo off
setlocal enableextensions enabledelayedexpansion
call :getargc argc %*
echo Count is %argc%
echo Args are %*
endlocal
goto :eof

:getargc
    set getargc_v0=%1
    set /a "%getargc_v0% = 0"
:getargc_l0
    if not x%2x==xx (
        shift
        set /a "%getargc_v0% = %getargc_v0% + 1"
        goto :getargc_l0
    )
    set getargc_v0=
    goto :eof

これは基本的にリスト(関数に対してローカルであるため、シフトがメインプログラムのリストに影響を与えない)に対して1回反復され、リストがなくなるまでカウントされます。

また、巧妙なトリックを使用して、関数によって設定される戻り変数の名前を渡します。

メインプログラムは、それを呼び出す方法を示し、後で引数がエコーされないようにします。

C:\Here> xx.cmd 1 2 3 4 5
    Count is 5
    Args are 1 2 3 4 5
C:\Here> xx.cmd 1 2 3 4 5 6 7 8 9 10 11
    Count is 11
    Args are 1 2 3 4 5 6 7 8 9 10 11
C:\Here> xx.cmd 1
    Count is 1
    Args are 1
C:\Here> xx.cmd
    Count is 0
    Args are
C:\Here> xx.cmd 1 2 "3 4 5"
    Count is 3
    Args are 1 2 "3 4 5"

これに基づいてフローをどのように変更できますか?(別の質問かもしれませんが、ここでは短い例も非常に便利だと思います!)
n611x007 '23

@ n611x007ここで、echo Count is %argc%カウントが適切かどうかを確認して次に進む独自のコードを挿入します。
ケニー

7

これを試して:

SET /A ARGS_COUNT=0    
FOR %%A in (%*) DO SET /A ARGS_COUNT+=1    
ECHO %ARGS_COUNT%

6
この答えは、@ nimrod oneとどういうわけか違いますか?...
青みがかった

1
nimrodmの@FerdinandBeyerによるコメントを確認してください。21
議員

6

引数の数が正確な数(9以下)でなければならない場合、これはそれをチェックする簡単な方法です。

if "%2" == "" goto args_count_wrong
if "%3" == "" goto args_count_ok

:args_count_wrong
echo I need exactly two command line arguments
exit /b 1

:args_count_ok

2

サイズと読みやすさを犠牲にして、どちらかshiftまたはforサイクルの使用を避けます。

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set /a arg_idx=1
set "curr_arg_value="
:loop1
if !arg_idx! GTR 9 goto :done
set curr_arg_label=%%!arg_idx!
call :get_value curr_arg_value !curr_arg_label!
if defined curr_arg_value (
  echo/!curr_arg_label!: !curr_arg_value!
  set /a arg_idx+=1
  goto :loop1
)
:done
set /a cnt=!arg_idx!-1
echo/argument count: !cnt!
endlocal
goto :eof

:get_value
(
  set %1=%2
)

出力:

count_cmdline_args.bat testing more_testing arg3 another_arg

%1: testing
%2: more_testing
%3: arg3
%4: another_arg
argument count: 4

編集:ここで使用される「トリック」には、以下が含まれます。

  1. 各ループの繰り返しでパーセント文字(%%)とカウンター変数を含む文字列を使用して、現在評価されているコマンドライン引数変数(「%1」、「%2」など)を表す文字列を作成しarg_idxます。

  2. その文字列を変数に格納しますcurr_arg_label

  3. その文字列(!curr_arg_label!)と戻り変数の名前(curr_arg_value)の両方をプリミティブサブプログラムに渡しget_valueます。

  4. サブプログラムでは、最初の引数の(%1)値が代入(set)の左側で使用され、2番目の引数の(%2)値が右側で使用されます。ただし、2番目のサブプログラムの引数が渡されると、コマンドインタープリターによってメインプログラムのコマンドライン引数の値に解決されます。つまり、渡されるのは、たとえば「%4」ではなく、4番目のコマンドライン引数変数が保持する値です(サンプルの使用法では「another_arg」)。

  5. 次に、戻り変数(curr_arg_value)としてサブプログラムに指定された変数が未定義であるかどうかがテストされます。これは、現在評価されているコマンドライン引数がない場合に発生します。当初、これは角括弧で囲まれた戻り変数の値と空の角括弧との比較でした(これは、引用符を含む可能性のあるテストプログラムまたはサブプログラムの引数を知る唯一の方法であり、試行錯誤の段階で見落とされていました)。それ以来、現在の状態に固定されていました。


1
このコードは質問に答えることがありますが、問題を解決する方法や理由に関する追加のコンテキストを提供すると、回答の長期的な価値が向上します。
thewaywereは

@thewaywerewereありがとう、私は多くの人(おそらく大多数)にとって「自明の」コードの代わりにテキストの説明を持つ方がよいことを忘れ続けています。
フェンファイヤー

@ fen-fire(コードが自明ではないことを除く)。
Loduwijk

1

最後の答えは今から2年前ですが、9つを超えるコマンドライン引数のバージョンが必要でした。もう一つかもしれません...

@echo off
setlocal

set argc_=1
set arg0_=%0
set argv_=

:_LOOP
set arg_=%1
if defined arg_ (
  set arg%argc_%_=%1
  set argv_=%argv_% %1
  set /a argc_+=1
  shift
  goto _LOOP
)
::dont count arg0
set /a argc_-=1
echo %argc_% arg(s)

for /L %%i in (0,1,%argc_%) do (
  call :_SHOW_ARG arg%%i_ %%arg%%i_%%
)

echo converted to local args
call :_LIST_ARGS %argv_%
exit /b


:_LIST_ARGS
setlocal
set argc_=0
echo arg0=%0

:_LOOP_LIST_ARGS
set arg_=%1
if not defined arg_ exit /b
set /a argc_+=1
call :_SHOW_ARG arg%argc_% %1
shift
goto _LOOP_LIST_ARGS


:_SHOW_ARG
echo %1=%2
exit /b

解決策は最初の19行で、すべての引数をcのようなスタイルの変数に変換します。他のすべてのものは結果を調べ、ローカル引数への変換を示します。任意の関数でインデックスを使用して引数を参照できます。


0

堅牢なソリューションは、で呼び出されるサブルーチンにカウントを委任することcallです。サブルーチンはgotoステートメントを使用してループをエミュレートしshift、(サブルーチンのみの)引数を繰り返し消費するために使用されます。

@echo off
setlocal

:: Call the argument-counting subroutine with all arguments received,
:: without interfering with the ability to reference the arguments
:: with %1, ... later.
call :count_args %*

:: Print the result.
echo %ReturnValue% argument(s) received.

:: Exit the batch file.
exit /b

:: Subroutine that counts the arguments given.
:: Returns the count in %ReturnValue%
:count_args
  set /a ReturnValue = 0
  :count_args_for

    if %1.==. goto :eof

    set /a ReturnValue += 1

    shift
  goto count_args_for
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.