pythonなら知ってる！人向けバッチファイルの解説

# 初めに

「バッチファイルの知識が必要になったけど何も分からない...けどpythonなら知ってる...」

という方向けに、バッチでできることをpythonに照らし合わせて紹介します。

いい感じでぬるりとバッチの世界に足を突っ込むことを目的としていますので、各セクションに対する網羅的な解説は行っておりません。

## Hello, world.

pythonの場合は、pythonをインストールした後に適当なエディタでコードを作成し、pythonコマンドで実行してあげる必要があります。

バッチファイルの場合は、メモ帳さえあれば実行することができます。簡単ですので、まずはバッチファイルの作成 → 実行までを行いたいと思います。

適当なテキストファイルを作成し、下記のような文字を入力してみましょう。

```bat
@echo off

echo Hello, world.
pause
```

※コードの内容が何を表すのかは現時点で分からなくでOKです。

上書き保存したあと、作成したテキストファイルの拡張子を「.txt」→「.bat」に変更します。

WARNINGが表示されますが、構わず「はい」を選択します。

![notion.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3337466/18c0539e-3030-81b0-0e91-a3594897a1c6.png)

すると、テキストファイルが歯車のアイコンに変わります。ダブルクリックしてファイルを実行してみましょう。

![@echo offあり.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3337466/6aa47719-88dd-11d0-9909-9887651a9dfb.png)

おめでとうございます。無事バッチファイルを起動させることができました。

`@echo off`は書いておくと幸せになれるコマンドです。もし記述してないとこのような出力結果となります。

![@echo off なし.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3337466/61f1cd22-f6a7-a03b-17c7-202f7a5ddda8.png)

いちいちコマンドを1行ずつ表示してしまうので、非常にくどい出力結果となってしまいます。

また「続行するには何かキーを押してください。。。」と表示されていますが、これは`pause`コマンドのおかげです。

これがないと、一瞬でプロンプト画面が閉じてしまいますので、処理の中身を確認することができません...。

## 標準出力

pythonの場合

```python
print('こんにちは')
```

バッチの場合は`echo`を使用します。

```bat
echo こんにちは
```

## 変数

pythonの場合

```python
greeting = 'こんにちは'
print(greeting)

>> こんにちは
```

バッチファイルの場合

```bat
set greeting="こんにちは"

REM 変数の中身を出力する場合は % で囲む
echo %greeting%

>> こんにちは
```

バッチの場合は `set` が必要です。

さらに、イコールの前後に空白を入れてはいけません。

```bat
REM これはOK
set greeting="こんにちは"

REM これはNG
set greeting = "こんにちは"
```

## コメントアウト

pythonの場合

```python
# これはコメントアウトです
```

上記ですでに登場していますが、バッチの場合は「**REM**」を使用します。

```bat
REM これがコメントアウトです
```

## for文

個人的に、今回紹介するバッチコマンドの中で一番厄介だと感じるのがこのfor文です。

この記事をご覧いただいている方にとっては何を今更感があると思いますが、pythonでのfor文はこんな感じです。

```python
animals = ['dog', 'cat', 'snake']
for animal in animals:
    print(animal)

>> dog
>> cat
>> snake
```

`in`の後ろにイテレータを用意してあげることで、中身をループ処理することができます。

すなわち、文にするとこうなります。

```python
for <入れ物> in <イテレータ>:
    いい感じの処理
```

イテレータの作り方や中身はその時々ですが、このおおまかな構文は変わりません。

バッチコマンドでも同じ要領でしたら私も幸せだったのですが、バッチコマンドではこの構文に**オプション**という概念が加わってきます。

すなわち、上記のイテレータ部分が同じ内容でも、**オプションによって出力結果が異なります。**

本記事では「バッチファイルのfor文なんか複雑で面倒」という認識さえ持っていただけたら幸いです。

ここからいくつか例を紹介しますが、詳細は[⧉こちら](https://qiita.com/plcherrim/items/67be34bab1fdf3fb87f9)の記事にて解説されておりますので、ぜひご覧ください（丸投げ）。

### ファイル名のループ

プログラムと同じディレクトリにあるファイル名をすべて出力します。

このようなディレクトリ構成だと仮定します。

```
./
├─main.py  ← 実行するpythonファイル
├─main.bat  ← 実行するバッチファイル
├─test01.txt
├─test02.txt
├─test03.txt
└─demo
```

pythonを使用して、ルートディレクトリにあるテキストファイル名を出力します。

```python
import os

text_filenames = list(filter(lambda x: str(x).endswith('.txt'), os.listdir('./')))
for filename in text_filenames:
    print(filename)

>> test01.txt
>> test02.txt
>> test03.txt
```

「.txt」で終了するファイル名を抽出しリスト化 → 順に出力しています。

続いてバッチファイルの場合です。

```bat
REM オプションなし
for %%i in ("*.txt") do (
	echo %%i
)

>> test01.txt
>> test02.txt
>> test03.txt
```

大雑把な処理として
1. 実行したバッチファイルと同じディレクトリにある「~.txt」ファイルを順番に`%%i`に格納し、
1. `echo %%i`で順番に出力しています。

このように、オプションがない場合は**ファイル名一覧**をイテレーションしてくれます。

ちなみに`*`は**ワイルドカード**のうちの一つで、「任意の文字0文字以上」を表します。pythonの`.+`みたいな感じですね。

ちなみに「任意の文字1文字」は`?`で表します。

### フォルダ名のループ

pythonを使用して、ルートディレクトリにあるフォルダ名を出力します。
すなわち「demo」のみ出力されればOKです。

```python
import os

dirs = [
    f for f in os.listdir('./') if os.path.isdir(os.path.join('./', f))
]
for folder_name in dirs:
    print(folder_name)

>> demo
```

`os.listdir()`メソッドで取得した要素を、`os.path.isdir()`メソッドでフォルダかどうか判断しています。

バッチですと、このように記述します。

```bat
REM /d オプション使用
for /d %%i in ("*") do (
	echo %%i
)

>> demo
```

**/d**コマンドがつくことにより、**探索対象がディレクトリ名のみ**になります。
「directory」の「d」なんですかね。

### 指定回数ループ

ループで一番想像しやすいのはこのパターンではないでしょうか。

```python
for i in range(0, 3, 1):
    print(f'{i} hello')

>> 0 hello
>> 1 hello
>> 2 hello
```

※バッチコマンドと比較するためにあえて冗長的に記述しています。

バッチファイルでは`/l`オプションを使用して実現します。

```bat
REM /l オプション使用
for /l %%i in (0,1,2) do (
	echo %%i hello
)

>> 0 hello
>> 1 hello
>> 2 hello
```

バッチにおける`in (0,1,2)`の部分は

```
(開始値、増分、終了値)
```

を表します。

pythonの**range()**の中身と記述方法が違うので注意です。

この記事がfor文の解説で占領されそうなので、例はこのあたりまでとします。
逆に言うとそれだけ密度のある内容ですので、ぜひ詳細記事をご覧いただく事を推奨いたします。

## if文

続いてif文による条件分岐です。

pythonの場合の例です

```python
weather = 'sunny'
if weather == 'sunny':
    print('プールに行こう')
else:
    print('スポッチャ行こう')

>> プールに行こう
```

同じ内容をバッチファイルで記述してみます。

```bat
set weather=sunny
if %weather% == sunny (
	echo プールに行こう
) else (
	echo スポッチャ行こう
)

>> プールに行こう
```

for文に比べてだいぶしっくりくるのではないでしょうか。
ifコマンドの基本は下記です。

```bat
if 条件 (
    条件が真だった場合の処理
else (
    条件が偽だった場合の処理
)
```

また、条件で使用できる**比較演算子**は下記のようなものがあります。

```bat
equ　等しい
neq　等しくない
gtr　より大きい
geq　以上
leq　以下
lss　より小さい（＝未満）
```

## 関数

受け取った引数の合計を返す関数を考えます。

```python
def add(v1, v2):
    return v1 + v2

result = add(2, 7)
print(result)

>> 9
```

バッチファイルの場合はこのように記述します。

```bat
@echo off

call :ADD 2 7 result

echo %result%

pause

exit /b

:ADD
	REM =================================
	REM args
	REM 	IN1, IN2: 足し算を行いたい数値
	REM 	IN3: 足し算結果を格納する変数名
	REM =================================
	set v1=%1
	set v2=%2

    REM 引数の内容確認
	echo %v1%
	echo %v2%

    REM 第3引数の名前で変数を作成し、第1, 2引数を足した結果を格納する
	set /a %3=%v1% + %v2%

	exit /b

>> 9
```

「:<処理名>」で、複数の処理を1まとめにして名称を付けることができ、**サブルーチン**と呼ばれます。

`call`コマンドを使用して呼ぶことができます。

```bat
call :<サブルーチン名>
```

また、サブルーチン名の後ろに半角スペース区切りで引数を渡すこともできます。
サブルーチン側では`%1`, `%2`, ...のように引数の順番を指定して受け取ることができます。

```bat
call :<サブルーチン名> hoge piyo

:<サブルーチン名>
    echo %1    >> hoge
    echo %2    >> piyo
```

お気づきかもしれませんが、サブルーチンでは返り値を指定することができません。
なので、:ADDには「足したい値×2」の他に「計算結果を格納したい変数名」を渡し、サブルーチン内で格納しています。

## まとめ

基本的なpythonの構文をベースに、バッチコマンドに置き換えた場合の解説をさせていただきました。

少しでも手助けになれば幸いです。

またご指摘等ございましたらぜひご教示ください。

---

株式会社ジールでは、初期費用が不要で運用・保守の手間もかからず、ノーコード・ローコードですぐに手元データを分析可能なオールインワン型データ活用プラットフォーム「ZEUSCloud」を提供しております。
ご興味がある方は是非下記のリンクをご覧ください：
https://www.zdh.co.jp/products-services/cloud-data/zeuscloud/