ari's world

あるかどうかわからないけど、あるみたい。ありがとう。

バッチファイルは Lightweight Language なのでしょうか

先日のエントリで「Windows バッチでたいていのことはできますね」と書きました。今週末に行われる Lightwight Language Ring のチケットを買ったので見に行こうと思っています。
Windows のバッチファイルは Lightweight Language なのか」どうかわかりません。「キミならどう書く 2.0 - ROUND 3 -」でグラフを出力するプログラムがお題でしたのでためしに Windows バッチで作ってみました。Windows があればコマンドプロンプトで動きますけど、おそらく Windows 以外の環境では動きません。現在、Windows Server 2003 を(クライアントとして)使用しているため、動作確認も Windows Server 2003 です。遅延環境変数を使用しているため cmd.exe /V:on と /V:on オプションをつけたコマンドプロンプトを起動する必要があります。

Lightweight Language Ring を楽しみにしております。

データファイル

空白の行を含まない適当なフォルダを作って、データファイルを作成します。テキストは、タイトル、タブコード、データ(0 以上の整数のみ)です。# で始まる行はコメントです。タイトルは 7バイト以下がいい感じです。


#comment
item1 1234
item2 5678
item3 910
item4 11121
とりあえず、こぴぺして date1.txt という名前で保存してください。
0 が最小値で、最大値の制限は調べておりません。たぶん、いくつでもいけるんじゃないかなと思っております。

graph.cmd

こぴぺして graph.cmd という名前で保存してください。
graph.cmd data1.txt のように使います。


@echo off
setlocal ENABLEDELAYEDEXPANSION

if "%1" EQU "" (
echo ERROR: option needed.
goto usage
)

if NOT EXIST "%1" (
echo ERROR: %1 is not found
goto usage
)

rem ===
rem 最大のアスタリスクの数をセットしてください。
set max_length=50
rem ===

set temp_data=temp_data.txt

for /F "eol=# tokens=1,2" %%i in (%1) do (
echo %%j>>%temp_data%
)

call %~dp0max.cmd %temp_data%
set /A rate=%max%/%max_length%

for /F "eol=# tokens=1,2" %%i in (%1) do (
call %~dp0line.cmd %%i %%j %rate% %%j
)

del %temp_data%

goto finish_point
:usage
echo アスタリスクでグラフを作りますファイルを指定してください。

:finish_point

max.cmd

こぴぺして max.cmd という名前で保存してください。
graph.cmd data.txt のように使います。最大値を環境変数 max に入れます。


@echo off
rem ======================
rem setting minimum value
set temp_max=0
rem ======================

if "%1" EQU "" (
echo ERROR: option needed.
goto usage
)

if NOT EXIST "%1" (
echo ERROR: %1 is not found
goto usage
)

for /F %%i in (%1) do (
if %%i GEQ !temp_max! (
set temp_max=%%i
)
)

rem echo temp_max: !temp_max!

set max=!temp_max!

set !temp_max!=

goto end_point

:usage
echo %~nx0 returns max value from LIST.FILE
echo USAGE: %~nx0 LIST.FILE
exit /B 1

:end_point

line.cmd

こちらは line.cmd で保存してください。


@echo off
setlocal

if "%3" EQU "" (
echo ERROR: option needed.
goto usage
)

set name=%1
set value=%2
set rate=%3

set /A stars=%2/%3
call %~dp0asterisks.cmd %stars%
if "%4" EQU "" (
echo %name% %asterisks%
) else (
echo %name% %asterisks% [%4]
)

goto finish_point
:usage
echo 一行描写します。みっつの引数で「名前」「値」「割合」を書きます。
echo よっつめの引数は値を書きます。
echo 内部的に使うので、いつも以上にいい加減です。

:finish_point

asterisks.cmd

こいつは asterisks.cmd で保存っと。


@echo off
rem =======
rem 引数に数を指定してください。
rem その数だけアスタリスクを asterisks 変数にセットします。
rem =======
set count=%1

set asterisks=:

if "%count%" EQU "" (
goto scenic_point
)

:start_point
if "%count%" EQU "0" (
goto scenic_point
)

set asterisks=%asterisks%*
set /A count=%count%-1
goto start_point


:scenic_point
rem echo %asterisks%

実行するだけ

cmd.exe /v graph.cmd data1.cmd

結果


D:\asteriskgraph>cmd /V:on
Microsoft Windows [Version 5.2.3790]
(C) Copyright 1985-2003 Microsoft Corp.

D:\asteriskgraph>graph.cmd data1.txt
item1 :***** [1234]
item2 :************************* [5678]
item3 :**** [910]
item4 :************************************************** [11121]

とりあえず、アップします。

作成時間 20分ぐらいでお昼休み中につくりました。グラフタイトルをつけたりエラー処理とかいろいろやりたいのですけれど、お昼ご飯を食べたいのでひとまずアップします。いくつかツールを使えば PNG/JPG/GIF なんかも作れそうです。ROUND1 や ROUND2 もバッチでかけそうだなぁ(にやり)。
お気づきの点や質問などは、コメントかトラックバックをいただければと思います。

追記 1 (8/23 13:00ごろ)

  • Lightweight のスペリングが間違っていましたので、訂正しました。はずかし……。

追記 2 (8/23 14:00)

  • line.cmd を「call %~dp0asterisks.cmd %stars%」とアップデートしました。以前のバージョンですと、カレントフォルダで実行する必要があります。
  • 保存するフォルダは空白の行を含まない必要があります。これは、単にクォートをし忘れたためです。なおしてのは後ほど時間があいてからにさせてください。
  • cmd /V:on オプションを忘れると「0 除算エラー」になります。これも回避方法を思いついたのですけれど、こちらも後ほど時間があいてから対応させてください。
  • データの最大値は 32 ビットで表記される数値が制限のようです。おそらく 32bit 版の Windows を使用しているためだと思います。

追記 3 ひとりごと
たとえば、max を得るのにWindows 標準ツールの sort を使って sort | tail -1 とか sort /r | head -1 なんか一行で終わってが一番簡単そうですよね。ただ、tail コマンドはリソースキットか cygwin を使う必要があります。tail か head をバッチで実装してしまうべきかなぁ。
どのあたりまで環境を予想するのかが難しいですね。
追記 4 その後
文字列として sort してしまうので、右寄せしてから sort しなくちゃいけないことに気がつきました。じゃくさんから教えていただいた setlocal ENABLEDELAYEDEXPANSION を graph.cmd につけてみました。どうもありがとうございました。
追記 5 再帰版です。

asteriks.cmd


@echo off
set /A count=%1 - 1
set asterisks=%2*

if %count% equ 0 goto finish_point

call %0 %count% %asterisks%

:finish_point
set count=

やっぱりきれいにかけますね。asterisks.cmd の再帰版は呼び出し方がかわります。10個のアスタリスクがほしいときは
asterisks.cmd 10 :
と書きます。