【shift利用なし】DOS（BATファイル）の引数をすべて取得（10個以上対応）【ワイルドカード】

## DOSの引数って面倒くさい

この記事を書いている中で、PHPやComposerをうまいこと複数バージョン対応にしたくて 「そうだ、PATHが通っているところにショートカットっぽく実行ファイルを置けばいいんだ」と気付き、試してみた。

https://qiita.com/bananacoffee/items/7dc8ee260050f13be372

例えば、以下の環境・要件のとき

- 環境
    1. `php` でPHP8.0のパスが通っていて `php7` でPHP7.4を実行したい
    1. php8.1は `D:\xampp\php\php.exe` に存在する（PATHが通っている）
    1. php7.4は `D:\xampp\php7\php.exe` に存在する（PATHは通っていない）
    1. `php -v` はPHP8.1が表示されるようになっている
- 要件
    1. `php7 -v` はPHP7.4が表示されるようにする（引数が1つ）
    1. **php7 コマンドの引数の数は予測できないがすべて利用できるようにしなければならない**
        - `php7 引数1 引数2 ... 引数X` が使えるようにする
        - **引数は10個以上の可能性もある**


### まずは1を解決（引数が1個）

これは簡単、 `D:\xampp\php\` に `php7.bat` を配置し、php7.4を実行してやれば良い

```D:\xampp\php\php7.bat
@echo off
echo execute: D:\xampp\php7\php.exe %1
D:\xampp\php7\php.exe %1
```

2行目は実運用では不要だが、何を実行したかわかるようにechoしている。
以下、実行結果

```sh
> php7 -v
execute: D:\xampp\php7\php.exe -v
PHP 7.4.22 (cli) (built: Jul 28 2021 09:44:30) ( ZTS Visual C++ 2017 x64 )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
```

間違いなくphp7.4が実行されている。

### DOSの引数は10個までしか扱えない？

たとえば、以下のコマンドを実行する場合

```sh
> php7 a b c d e f g h i j k l
```

こうすれば行けそうな気がするが

```D:\xampp\php\php7.bat
@echo off
echo execute: D:\xampp\php81\php.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12
D:\xampp\php7\php.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12
```

こう実行されてしまう

```sh
> php7 a b c d e f g h i j k l
execute: D:\xampp\php7\php.exe a b c d e f g h i a0 a1 a2
```

`a0 a1 a2` ってなんだ？と思うが、これは、`%n` のn部分に2文字が利用できないため。
つまり '%10' は　`引数%1 + 数字の0` という扱いになってしまう。
しかも引数が少ないと

```sh
> php7 a b c
execute: D:\xampp\php7\php.exe a b c       a0 a1 a2
```

こうなってしまうため、要件2の **引数の数は予測できない** にマッチしない。
（10個以内ならマッチするが、無駄な半角スペースが入る）
そこで、下記記事のように`shift`を使うことで回避ができる。

https://jj-blues.com/cms/wantto-morethan10arguments/

https://qiita.com/koiwaiwaiko/items/f47e9cd25f61a82435d4

つまり、こうすれば良い（前者の記事から引用して改変している）

```D:\xampp\php\php7.bat
@echo off
 
set count=1
set argv=

:loop
if "%1"=="" goto :confirm
set argv=%argv% %1%
set /a count+=1
shift
goto loop
 
:confirm
echo execute: D:\xampp\php81\php.exe%argv%
D:\xampp\php7\php.exe%argv%
```

これで `php7 -S localhost:8000` や `php7 artisan breeze:install react` のようなケースにも対応できる。

:::note warn
注意
laravelはcomposer側に工夫が必要なので、**引数が使えるというだけで、このままで正常動作するわけではない**。
:::

## 引数にワイルドカードが使えるじゃん！

ここまででひとまず完成ではあるが、同じことをcomposerで設定しようとしたところ、`composer.bat`に以下のような記述があった。

```composer.bat
@echo OFF
:: in case DelayedExpansion is on and a path contains ! 
setlocal DISABLEDELAYEDEXPANSION
php "%~dp0composer.phar" %*
```

**なんと、`%*` が使えるではないか**

「DOS 引数」でググっても基本的には上記記事のようなshiftで対応するものばかりなで、ソースが見つけられなかった。
唯一見つけた以下の記事を参照すると

https://eel3.hatenablog.com/entry/20080727/1228921505

> バッチ パラメータ %* は、バッチ ファイルに渡される %0 を除くすべての引数を参照するワイルドカードです。

と記載されている（`%0`については後述）。
この記事が2008年なので、WindowsXP ～ 7の間くらいで採用されたか、NT系の標準仕様なのかもしれない。
早速、試してみよう

```D:\xampp\php\php7.bat
@echo off 
echo execute: D:\xampp\php81\php.exe %*
D:\xampp\php7\php.exe %*
```

```sh
> php7 a b c d e f g h j k l             
execute: D:\xampp\php7\php.exe a b c d e f g h j k l
```

なんとたったの3行で実行できた。
試しにちゃんと実行できる引数で試してみる。

```sh
> php7 -v
execute: D:\xampp\php7\php.exe -v
PHP 7.4.22 (cli) (built: Jul 28 2021 09:44:30) ( ZTS Visual C++ 2017 x64 )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

> php7 -S localhost:8000
execute: D:\xampp\php7\php.exe -S localhost:8000
[Fri Sep  9 11:25:56 2022] PHP 7.4.22 Development Server (http://localhost:8000) started
```

**完璧！**
composerさん、大切な気付きをくれてありがとう。
`shift`を駆使している皆さん、早く気づいて！


## 番外：引数9個しか使えてなくない？(%0について)

説明を省いたが `%0` が存在し、コマンド（batファイル）の（フル）パスが引数となる。
本件の場合は `D:\xampp\php\php7.bat` になる。
使わないので省略しているがこれで引数の合計が10個となる。