Windowsの話です。アクロバチックなルーチン作った話です。
製造業で働いてますが、ある事情から社内工程パトロールなんてことをやらなければならない羽目になりました。そんなものやってもやらなくても関係ないクズ職場なんですが、それについてはここでは多く触れません。
今回思いついたアイデアは工程パトロールするのにどうしたら忘れにくく「ハッ」やらねばって思い出せるかなあって考えて作ったルーチン処理です。
デスクトップに(仮に毎週)あたらしいショートカットファイルが出来ていたら「あ、これやらないといかんな」と思い出せるかなと思ったのです。
別にほかのやり方でもいいんですけど、なんとなくどうやっても面倒くさそうだった。工程パトロールチェックをするための雛形のEXCELシートファイルは既にあって、それに直接書き込むと名前を変えて保存とかしなきゃならないじゃないですか。それがまずメンドくせー。億劫です。
かといって今年専用のチェック用EXCELファイルにシートをどんどん増やしていくのもなにやら面倒くさい。とにかくあらゆる面倒臭さを排除して常にそこにあるファイルを開けば名前のついた最新のまっさらファイルになっていて書き込んで保存して、(仮に翌週)になったらまた新しいファイルがそこに用意されているという状況を作りたかったんです。
ここに行き着くまで実働7時間ぐらい、日数にしてのべ2日ぐらいかかりました。まあ時間のかからなかったほうではないかと思いますが、調べまくったこと著しい。ブラウザのタブが数え切れないほど溜まりました。
という前置きではありますがそんな機能ほしかったんだよ~という人以外はまったく無意味な内容かもしれません。
雛形ファイルから日付付きファイルへコピーしてデスクトップにショートカットつくる自動化バッチ
原理なんてどうでもいいから使わせろって人むけにソース
そういう人いますか。ぼくも半ばそうです。くどくどした説明はいいからどこをコピペすればいいのかだけ教えろ!というタイプの人はこれを読んでくれ。
一応最低限の処理の流れを示します。
main.cmd | → | GetIni.cmdファイル情報ファイル読み込み |
→ | csvファイル吐き出し | |
→ | csvtab.exe(csvからタブ区切りテキストへ変換) | |
→ | shortcut.wsf(ショートカットをデスクトップに生成) |
まずはメインとなるバッチファイル。あとはこれを呼び出すタスクスケジューラーをセットするだけ、簡単でしょ。と言いたいところですが少しこまごました設定があります。でもまずは以下のバッチファイルがメインの呼び出し元になります。
@echo off rem INIファイルの取り込みと変数への格納 set INI_FILE_FULLPATH=make_shortcut.ini call GetIni.cmd :READ_INI_VAL "FILE" "PROTOTYPE_PATH" GET_VAL1 %INI_FILE_FULLPATH% echo %GET_VAL1% call GetIni.cmd :READ_INI_VAL "FILE" "DESTINATION_PATH_1" GET_VAL2 %INI_FILE_FULLPATH% echo %GET_VAL2% call GetIni.cmd :READ_INI_VAL "FILE" "DESTINATION_PATH_2" GET_VAL3 %INI_FILE_FULLPATH% echo %GET_VAL3% call GetIni.cmd :READ_INI_VAL "FILE" "SHORTCUT_NAME" GET_VAL4 %INI_FILE_FULLPATH% echo %GET_VAL4% call GetIni.cmd :READ_INI_VAL "FILE" "SHORTCUP_CMNT" GET_VAL5 %INI_FILE_FULLPATH% echo %GET_VAL5% call GetIni.cmd :READ_INI_VAL "CONFIG" "CSVTAB_PATH" GET_VAL6 %INI_FILE_FULLPATH% echo %GET_VAL6% rem 日付取得してスラッシュ(/)除去→変数へ格納 set YYYYMMDD=%date:~0,4%%date:~5,2%%date:~8,2% rem 雛形からデータフォルダへコピー copy "%GET_VAL1%" "%GET_VAL2%%YYYYMMDD%%GET_VAL3%" rem ファイルの最後に改行が入らないようにする呪文 set /p NOLINEBREAK=%VERSION%< nul rem CSV型式の定義ファイルへ吐き出し rem shortcut.wsfに渡すためのファイルだけどタブ文字が吐き出せないので rem 「,」で区切った文字列をechoしてファイルに吐き出す echo %YYYYMMDD%%GET_VAL4%,%GET_VAL5%,"%GET_VAL2%%YYYYMMDD%%GET_VAL3%",> config.csv rem 吐き出したファイルの「,」をタブ文字に変換するコマンド echo %GET_VAL6%\csvtab.exe call %GET_VAL6%\csvtab.exe config.csv config.dat -dbl_no_cut rem 変換されたファイル「config.dat」の情報を元にデスクトップ rem にショートカットを作るスクリプト call .\shortcut.wsf
▼INIファイルの中には以下の項目を入れておきます。
comment1〜5はコメントを書いているだけですので消してしまっても構いません。
DESTINATION_PATH_2=.xlsxというのはコピー先の拡張子で本来ならコピー元の拡張子が決まっていれば同じなのですが、それをプログラムで解析するのが面倒だったのでここで指定する形をとっています。スマートじゃないと思いますので誰かコピー元ファイルの拡張子部分だけ取り出す処理の仕方を教えて下さい。
[FILE] comment1=PROTOTYPE_PATH=コピー元の雛形ファイル名のパス PROTOTYPE_PATH=¥¥server¥prototype¥SAMPLE.xlsx comment2=DESTINATION_PATH_1=コピー先のファイル名のパスと識別名(識別名は省略可) DESTINATION_PATH_1=¥¥server¥data¥2016 作業記録¥RECORD comment3=DESTINATION_PATH_2=コピー先ファイルの拡張子 DESTINATION_PATH_2=.xlsx comment4=SHORTCUT_NAME=ショートカット SHORTCUT_NAME=作業パトロール comment5=SHORTCUP_CMNT=ショートカットのコメント SHORTCUP_CMNT=作業規格通り作業しているかパトロールした記録 [CONFIG] CSVTAB_PATH=¥¥cadserver¥design¥data¥アプリソフト¥csvtab106
▼続いてはINIファイルから項目を読み込むサブルーチンとして使うバッチファイル。INIファイルを作っておいて個別情報を入れておけば汎用的に使うことができます。つまり貴方だけでなく誰か他の人も業務で利用させてあげることが容易になります。内容についてはぼくはまったく理解できませんですがコピペして保存するだけですぐ使えます。
@echo off REM 遅延環境変数を使うためにこれを書いておく REM 遅延環境変数(%HOGEHOGE%ではなく!HOGEHOGE!と書く)についてはググってね setlocal enabledelayedexpansion :READ_INI_VAL REM ==================================================================== REM INIファイルから項目を読み取り返す REM REM ※キーを取得できない場合は、取得変数に「ERR」を返す REM REM ==================================================================== REM REM 他のbatから以下のようなコーリングシーケンスでコールされる事を期待している REM call GetIni.bat :READ_INI_VAL "SECTION名" "KEY名" GET_VAL %INI_FILE_FULLPATH% REM REM 引数の説明 REM %0 : (in ) このファイルの名前 (例:「GetIni.bat」) REM %1 : (in ) このサブルーチン名 (例:「:READ_INI_VAL」) REM %2 or %~2 : (in ) セクション名 (例:「SECTION名」) REM %3 or %~3 : (in ) キー名 (例:「KEY名」) REM %4 : (out) 取得データをセットする変数 (例:「GET_VAL」) REM %5 : (in ) iniファイルのフルパス (例:「c:\hoge\foo\fuga.ini」) REM REM ※指定したSECTION名やKEY名が取得できない場合は取得データをセットする変数に「ERR」をセットする REM REM ==================================================================== REM REM for命令についての補足 REM /F の時に使用できるオプション REM REM eol=c : 行末コメント開始文字をcにする。この文字以降は注釈として無視される REM skip=n : ファイルの先頭からn行を無視する REM delims=xxx : デリミタをxxxとする。複数文字の指定が可能。デフォルトはタブとスペース REM tokens=x,y : どのパートを変数に代入してコマンド側に渡すかを指定する。 REM usebackq : バッククォート(“`”、逆引用符)で囲まれた文字列をコマンドとして実行する REM REM http://www.atmarkit.co.jp/ait/articles/0106/23/news004_2.html REM REM ==================================================================== REM ------------------------------------------------ REM ファイルを1行ずつ読み出して、検索 REM ------------------------------------------------ set GETINIVALUE= set SECTIONNAME= REM 「%%x」変数にキー名が、「%%y(←自動で作られる)」変数にキーの値が入る事を期待している REM REM 「delims==」は、「delims = (イコール記号)」と読めば良い。要は「=」を区切り文字としている REM INIファイルのキーは「KEYNAME=5」みたいな形になっているので、KEYNAMEを%%xへ、5を%%yへセットすることを意味している REM REM 「tokens=1,2」はdelimsで区切った1個目を%%xへ、2個目を%%yへという意味 REM REM %5はINIファイルのフルパスで、このfor文はINIファイルから一行ずつ取得している for /F "eol=; delims== tokens=1,2" %%x in (%5) do ( REM ------------------------------------------------------ REM 文字の抽出について REM REM %V% 変数Vの値全体 REM %V:~m% m文字目から、最後まで REM %V:~m,n% m文字目から、n文字分 REM %V:~m,-n% m文字目から、最後のn文字分を除いたもの REM %V:~-m% 後ろからm文字目から、最後まで REM %V:~-m,n% 後ろからm文字目から、n文字分 REM %V:~-m,-n% 後ろからm文字目から、最後のn文字分を除いたもの REM %V:c1=c2% 文字c1を文字c2に置換する。それぞれ複数の文字を指定することも可能 REM REM https://www.upken.jp/kb/kZwpzAqblKfZDjtMXuWuwioeExKNdE.html REM ------------------------------------------------------ REM 取得した行がキーの行であれば、キー名が入るはず set KEYNAME=%%x REM !KEYNAME:~0,1! : 先頭一文字を取得→取得した行がセクションの行であれば、[ REM !KEYNAME:~-1,1! : 最終一文字を取得→取得した行がセクションの行であれば、] set P=!KEYNAME:~0,1!!KEYNAME:~-1,1! REM []の中の文字を取得(セクション名を期待している) set S=!KEYNAME:~1,-1! REM "[]"はセクション名を意味する if "!P!"=="[]" set SECTIONNAME=!S! REM セクション名とキー名が引数で指定されたものと一致すればOK! REM で、処理終了 if "!SECTIONNAME!"=="%~2" if "!KEYNAME!"=="%~3" ( set GETINIVALUE=%%y goto GET_INI_EXIT ) ) REM ------------------------------------------------ REM 項目が見つからない場合は、「ERR」を変数へ入力 REM ERRではなく何もセットしたくなければ、 REM 「set GETINIVALUE=」と書けば良い REM ------------------------------------------------ set GETINIVALUE=ERR REM setlocal~endlocal間(以下、local内)は通常のプログラム言語で言うところの関数のようなもの REM local内を出る時にはlocal内の環境変数は消えてしまう REM 「GETINIVALUE=%GETINIVALUE%」は、local内の%GETINIVALUE%を、 REM 新しいGETINIVALUEへセットしていると考えれば良い REM REM :GET_INI_EXITラベルの下にendlocalを書くのは気持ち悪いが、実行タイミング的にここしか無い :GET_INI_EXIT endlocal && set GETINIVALUE=%GETINIVALUE% REM ------------------------------------------------ REM 取得変数名にセット REM コール側でこの値が取れる REM ------------------------------------------------ set %4=%GETINIVALUE% :EOF REM /b オプションを外すとこのbatをコールしているbatまで死ぬ exit /b
さきほど申しましたようにこのバッチファイル(GetIni.cmd)はなんの手も加えていないコピペです。
参照サイトはiniファイルを読み込むbatファイルの作り方。今の時代でも充分役に立つゾ!(タブンね・・・) | Gabekore Garageです。ありがとうございます。
▼そして任意の場所にあるファイルからデスクトップにショートカットを置く処理はこちらを参照しました。万一参考サイトの記事が消えてしまったときのためにコードを掲載させていただきます。
<?xml version="1.0" encoding="Shift_JIS" standalone="yes" ?> <package> <job id="shortcut"> <?job error="true" debug="true" ?> <object id="objFs" progid="Scripting.FileSystemObject" /> <script language="VBScript"> <![CDATA[ ' ショートカットの情報を記述した設定ファイルの格納場所 ' 設定ファイルのパスについては、各自の環境に合わせて変更する必要がある Const CONFIG="C:\config.dat" Set objTs=objFs.OpenTextFile(CONFIG,1,False) ' config.datの情報に基づいて、ショートカットを作成する ' config.datにはタブ区切りテキスト形式で、ショートカットのファイル名、コメント、リンク先のパス、ホット・キーが定義されているものとする。 Do While Not objTs.AtEndOfStream aryDat=Split(objTs.readLine,Chr(9)) Set objShl=WScript.CreateObject("WScript.Shell") Set objCut=objShl.CreateShortcut(objFs.BuildPath(objShl.SpecialFolders("Desktop"),aryDat(0) & ".lnk")) ' デスクトップ上にショートカットを作成 objCut.Description=aryDat(1) ' コメント objCut.TargetPath=aryDat(2) ' リンク先のパス objCut.Hotkey=aryDat(3) ' ホット・キー(Alt+、Ctrl+、Shift+、Ext+) ' 作業フォルダ(My Documents) objCut.WorkingDirectory=objShl.SpecialFolders("MyDocuments") objCut.Save ' 生成したショートカットを保存 Loop objTs.Close ]]> </script> </job> </package>
@IT:Windows TIPS — Tips:デスクトップ上に必要なショートカットを自動生成する
あとはcsvtab.exeというのをネットから探してきて任意のフォルダーにインストール(置くだけ)すること。
CSVファイルTABファイル変換プログラムの詳細情報 : Vector ソフトを探す!
INIファイルの取り込みと変数への格納
では1つずつ処理を解釈していきます。あとで自分で読み返して再利用できるように丁寧に書くつもり。
iniファイルを読み込むbatファイルの作り方。今の時代でも充分役に立つゾ!(タブンね・・・) | Gabekore Garage
GetIniというバッチファイルを作られた方がいたようですが、まさかバッチファイルだけで.iniファイルの項目を読めるとは知りませんでした。こういうことができるといろいろと応用範囲が広がりそうです。
ここのINIファイル取り込みではmake_shortcut.iniというファイルに書いてある固有のフォルダ情報などを読み込みます。フォルダ名やファイル名にスペースが入っていても大丈夫そうです。おそらく改行までが一つの情報の塊として認識されるんじゃないかな。
日付取得してスラッシュ(/)除去→変数へ格納
ここの部分は文法を調べればわかるのですが、どこかの親切なサイトからもらってきてそのまま使ってます。記事を書く今になって探したらもっと簡単な記述が見つかりました。
バッチファイルで今日の日付 (YYYYMMDD 形式) を取得 – アジャイルプログラマの日常
雛形からデータフォルダへコピー
ここの1文は単純にGetIniで取り出した情報をくっつけてコピーコマンドのパラメーターを作っているだけです。
文法は今さらですが、
copy コピー元 コピー先
ファイルの最後に改行が入らないようにする呪文
▼タブ区切りファイルを生成するときに最後に改行なのかスペースなのかわからないものが混ざってしまって上手く動かないときがあったので、ファイルの最後に改行が入らないようにする呪文を調べて入れました。実際にはいらないかもしれませんし結局改行が入ってしまってます。スペースの除去は次の行でechoをファイルにリダイレクトするときスペースをつけず密着させて記述したら解決しました。
CSV型式の定義ファイルへ吐き出し
本当はここで項目がタブで区切られたテキストを直接生成したかったのですが、どうにもタブ文字がテキストファイルに吐き出せなくて仕方なく「,(カンマ)」区切りのファイルを作ってます。
echoコマンドでパラメーターの順番はショートカット名,ショートカットのコメント,ショートカットの元ファイルのパス,ホットキー
コメントとホットキーは省略できますが区切りは必要です。
CSV定義ファイルからタブ区切りのテキストへ吐き出し
CSV形式のファイルからタブ区切りのテキストへ変換またはその逆変換をしてくれるフリーソフトを見つけたのでこれを活用させてもらいます。
CSVファイルTABファイル変換プログラムの詳細情報 : Vector ソフトを探す!
定義ファイルを元にコピーされたファイルからデスクトップへショートカットを作る
これもググっていたら実に便利なスクリプトを作って紹介しているサイトがあったので活用させてもらいました。
@IT:Windows TIPS — Tips:デスクトップ上に必要なショートカットを自動生成する
タイマーで定期的に実行するために
もし社内ネットワークを利用しているとしたら仮想ドライブを割り当てておいたほうが良いかもしれません。すみませんが¥¥指定のネットワークパスですべていけるか未確認です。
▼ただ気をつけることはバッチファイルやスクリプトファイル、またcsvtabというexeファイルを実行するのにそれらの実行ファイルが必ず意図したフォルダに生成してくれるか不安なので一応ぼくはその実行する一連のファイル群を置いた場所にYドライブと名付け、タスクスケジューラーでの開始オプションでY:と指定しました。ただしこれをやらないとちゃんと動かないかという検証はしていません。
まとめ
わかりにくい部分もあるでしょうし、コード内にechoなんて入っていて標準出力に出す命令が残っている箇所もありますが、適当に改造してください。
たぶんこの記事に行き着いた人はそれくらいの読解力は持ち合わせていると思います。
いやーバッチファイルって面白いですね。昨今のコンピューターの動きはすべてウィンドウを開いてダイアログによる命令ばかりですが、こうしたコマンドの羅列による動きって自動化させてるぜ!って感じでアゲアゲです。