命名って難しい

変数、関数、クラスなどなど実装より命名に毎回悩むタイプの人間による技術についてのメモ。

社内のイントラネットを更新のある時だけ開きたいというバッチ。

イントラネットのトップページに色々更新情報があるんですが、 更新情報が無い時も開いて目視しているのが面倒なので、 更新がある時だけ開くバッチを作りました。

処理の流れ

  1. 以前取得していたページがあればそれを前回取得したページとして名称変更
  2. 現在の対象ページを取得する
  3. 以前取得した対象ページと比較する
  4. 一致してなければそのページを開く

以上。

ソースコード

@echo off
setlocal
pushd %~dp0

set url=%1
set compare_log_path=%~dp0compare_log.txt
set current_page_path=%~dp0current.html
set previous_page_path=%~dp0previous.html

rem 前回取得したままの現在のファイルを前回のファイル扱いにする。
move  /Y %current_page_path% %previous_page_path%

rem 対象ページを取ってくる
bitsadmin /TRANSFER check_intra %url% %current_page_path%

rem 以前取得したページと比較する
fc %current_page_path% %previous_page_path% > %compare_log_path%

rem 一致しない時は開く
if %errorlevel% == 0 (
  goto end
) else (
  start /d "C:\Program Files\Internet Explorer" IEXPLORE.EXE %url%
)

:end
popd
endlocal

呼び出しているプログラム/参考

bitsadmin

wget的なものが欲しかったので使用。 簡単な使い方しかしてないけれど色々機能あるみたいですね(全部読む気力はない)。 取得先のファイルが既に存在すると上書きしてくれないみたい。

BITSAdmin Tool
BITSAdmin Examples

fc

ファイルの比較で使用。 比較結果は一応ログにしてますけど特に意味は無いです。

FC.exe

使いかた

 [このバッチ] "[url]"

以上

Windowsのバッチのライブラリを作り始めた(ヘルプ出力バッチ&テキストの行数出力バッチ)

なお不定期。

業務で使う特定の処理のバッチを作ってライブラリにしようかなと思って少し書いてみた。 ブログのタイトルにもしてるんですけど、なんでも作る時に命名が難しすぎますね。。。 まだライブラリ未満です。

将来これを拡張して便利なものに~~~とか思ってたんですけど 既にあったりするのかな。 勉強にはなったけれど貢献になるか割と微妙ですね!

面白かったのでとりあえず記事に残しときます。

ヘルプを出力するバッチ

概要

ヘルプの前提は以下です。

  • バッチの中にヘルプ文言を全部書く。
  • ヘルプは引数エラーの時のみ出力する(/h)など未対応

ファイル名称

ShowHelp.bat

ソースコード

@echo off
setlocal enabledelayedexpansion

if "%1"=="" (
  echo 引数が不正です。ヘルプを表示します。
  "%~f0" "::" "%~f0"
  goto :EOF
)

goto TopOfCode

::ShowHelp.bat - 指定のバッチファイルのヘルプを表示します。
::
::使用法
::    ShowHelp [コメントアウト文字列] [ファイルパス] 
::
::詳細
::    指定のバッチファイル内のコメントアウト文字列を使ったヘルプを表示します。
::
::例
::    以下の内容のファイルのヘルプを表示する時
::      対象ファイル    :Test.bat
::      コメントアウト文字列://
::    "ShowHelp.bat" "//" "Test.bat"

:TopOfCode

set annotation=%~1
set filepath=%~f2
for /f "usebackq delims=" %%a in (`findstr /R "^%annotation%" "%filepath%"`) do (
  set cmt=%%a
  set cmt=!cmt:%annotation%=!
  echo.!cmt!
)

endlocal

テキストファイルの行数を出力するコマンド

概要

Windowsバッチにはwcがないのでつくりました。 行数のみをぱっと出力するコマンド。

ファイル名称

CountLine.bat

ソースコード

@echo off
setlocal

REM 引数
set drive=%~d1
set filePath=%~f1

if "%filePath%"=="" (
  echo 引数が不正です。ヘルプを表示します。
  ShowHelp "::" "%~f0"
  goto :EOF
)

if not exist "%filePath%" (
  echo.0
  goto :EOF
)

goto TopOfCode

::CountLine.bat
::
::使用法
::    CountLine [TEXT FILE] 
::詳細
::    パラメータで指定したテキストファイルの行数を取得します。
::

:TopOfCode

pushd %~dp0

REM 指定のファイルパスがUNC形式の場合、参照する場所が違う。
set tokensNumber=3
If "%drive%"=="\\" set tokensNumber=2

for /f "usebackq tokens=%tokensNumber% delims=:" %%a in (`find /v /c "" "%filePath%"`) do (
  set count=%%a
)

echo %count: =%

popd
endlocal

以上!

Outlookの規定の予定表にある各予定表の予定一覧を出力する。

特化しすぎてタイトルが長い!

概要

Outlookの予定表を複数用意して、定時タスクを登録している。 複数の予定表から特定の日付のタスクを全部リストアップしたい。

ソースコード

以下のソースで適当にvbs作って呼ぶだけ。
必要に応じてテキストにリダイレクトでもすればいい。

cscript //nologo [VBSファイル名] [取得したい日付] [取得したい予定表の名前(N個)] 
' 引数チェック!
if WScript.Arguments.Count < 2 Then
  WScript.Echo "引数が少なすぎます。 [日付] [予定表名(任意の数)]"
  WScript.Quit
End if
Set args = WScript.Arguments
Set objOutlook = CreateObject("Outlook.Application")
Set objNamespace = objOutlook.GetNamespace("MAPI")
Set defFolder = objNamespace.GetDefaultFolder(9)

' 指定の日付(第一引数)
dt = CDate(args(0))
' それ以降の対象の予定表名をループ
For i = 1 to args.Count - 1
    calName = CStr(args(i))
    Set calFolder = defFolder.Folders(calName)
    Call EchoEvents(calFolder, dt)
Next

' 指定のカレンダーの指定の日付のイベントを出力
Sub EchoEvents(calFolder, dt)
    Set colItems = calFolder.Items
    calName = calFolder.Name
    ' 普通のイベント
    For Each objItem In colItems
        If GetDateOnly(objItem.Start) = GetDateOnly(dt) Then
            ' CSV形式で [予定表名],[予定名],[開始時刻] で出力
            WScript.Echo calName & "," & objItem.Subject & "," & Hour(objItem.Start) & ":" & Minute(objItem.Start)
        End If
    Next
    
    ' 定期イベント
    Set colFilteredItems = colItems.Restrict("[IsRecurring] = TRUE")
    For Each objItem In colFilteredItems
        ' DayOfWeekMaskの各曜日の値はWeekDayで取得できる数値から1引いて2乗した値になる。
        maskValue = 2^(Weekday(dt) - 1)
        Set objPattern = objItem.GetRecurrencePattern
        ' ANDで一致すればOK
        If objPattern.DayOfWeekMask AND maskValue = maskValue Then
            ' CSV形式で [予定表名],[予定名],[開始時刻] で出力
            WScript.Echo calName & "," & objItem.Subject & "," & Right("00" & Hour(objItem.Start), 2) & ":" & Right("00" & Minute(objItem.Start), 2)
        End If
    Next
End Sub

' 日付のみ取得。DateTimeをシンプルなDateに変える。
Function GetDateOnly(dt)
  GetDateOnly = CDate(Year(dt) & "/" & Month(dt) & "/" & Day(dt))
End Function

(技術メモ)(SQL Server 2000/2012)ログインの追加→DBへのユーザーの追加→ユーザーのロール設定までの流れ

概要

ちょっとしたDBリプレースで必要になったのでメモ。
これをうまいことコードで管理して同一構成のサーバーをすぐにセットアップできるようにしたい。

SQL Server 2000

t-sql

-- masterでログインを追加
use master;
EXECUTE sp_addlogin 'NEW_LOGIN', 'NEW_LOGIN_PASS', 'NEW_DB';

-- 指定のDBでそのログインに合わせたユーザーを追加
use NEW_DB;
EXECUTE sp_adduser 'NEW_LOGIN', 'NEW_USER';

-- 読み書き自由自在のユーザーにしたい
EXECUTE sp_addrolemember db_datareader, 'NEW_USER';
EXECUTE sp_addrolemember db_datawriter, 'NEW_USER';

参考URL

sp_addlogin (Transact-SQL)

sp_adduser (Transact-SQL)

sp_addrolemember (Transact-SQL)

SQL Server 2012

t-sql

-- masterでログインを追加
use master;
CREATE LOGIN NEW_LOGIN WITH PASSWORD = 'NEW_LOGIN_PASS', DEFAULT_DATABASE =NEW_DB;

-- 指定のDBでそのログインに合わせたユーザーを追加
use NEW_DB;
CREATE USER NEW_USER FROM LOGIN NEW_LOGIN;
-- 読み書き自由自在のユーザーにしたい
EXECUTE sp_addrolemember db_datareader, 'NEW_USER';
EXECUTE sp_addrolemember db_datawriter, 'NEW_USER';

参考URL

CREATE LOGIN (Transact-SQL)

CREATE USER (Transact-SQL)

RedmineのガントチャートPNG出力の日本語文字化けを直す

現象

ガントチャートPNG出力すると日本語が文字化けする

環境

こちらを使用しています。 blog.enjoyxstudy.com

行った作業

まず公式の解説
ガントチャートをPNG形式の画像に出力すると文字化けする — Redmine.JP

足りない事は以下

  • rmagickのインストール
  • 日本語フォントのインストール
  • redmine再起動

rmagickのインストール

gem install magick

で、できなかったのでsudo付けてgemしたらコマンドがないと・・・
結論としてはsudo付けてgemを直接パス指定して実行しました。
ここらへんはどういうことなのか調べる必要がある。

日本語フォントのインストール

wgetして /usr/share/fonts/japanese/ 直下にファイルを移動
IPAフォントのダウンロード

で、フォントキャッシュを再構成

fc-cache -fv

redmine 再起動

redmine直下のtmpフォルダにrestart.txtを作るだけで次アクセスした時に再起動

以上。

業務でCsvHelperを使った感想

CsvHelperとは

.Net向けのCSV読み書きライブラリです。 公式ページ

前提

以下のようなツールの開発で使用しました。

  1. CSVから情報を取り込む

  2. CSVの情報からなんやかんやする

  3. なんやかんやしたCSVデータをDBに保存する

感想

CSVの取込処理の負担軽減

社内ライブラリ、CSVの行をテキストで取った前提で、それをパーズしてインデックスでアクセスするようなクラスしかないんで…
単純にこれを使うだけでも恐ろしい負担軽減になりました。

CSVレコードからオブジェクトへのマッピングが容易にできる

前述の通り、社内ライブラリが貧弱なので行単位でテキストをオブジェクトにするため、
文字列からキャストやらパーズやら・・・という事をしていました。
CsvHelperはマッピングの設定を行うだけで、CSVからそのオブジェクトのリストに変換してくれます。
列名がオブジェクトのプロパティ名と同様であれば、マッピングも必要なかったかと思います。

今回はCSVに重複レコードがあったりもしたので、列のインデックスでマッピングを設定できる所に助かりました。
Dapperとの相性もよし!

サンプルコード

// こんな感じでマッピングをクラスで定義する。
public class MyCsvMapper : CsvHelper.Configuration.CsvClassMap<MyCsvRecord>
{
    public MyCsvMapper()
    {
        // こんな感じでマッピングを追加していく。
        Map(o => o.ItemName).Name("商品名");
        Map(o => o.Price).Index(2);        
    }
}

// CSV読み込みクラス
public class CsvReader
{
     public void Read()
     {
         using(var sr  = new StreamReader(@"ファイルのパス", Encoding.GetEncoding("SHIFT_JIS")))
         using(var csv = new CsvHelper.CsvReader(sr))
         {
             csv.Configuration.HasHeaderRecord = true;
             csv.Configuration.RegisterClassMap<MyCsvMapper>();
             var csvRecords = csv.GetRecords<MyCsvRecord>().ToList();
         }
     }  
}

SQLServerの権限(許可/拒否/取消)

経緯

SQL Serverのテーブルに対する権限の管理をSSMSでクリックして選んでやってたのですが、 コマンドの方が楽だと思い調べる。 今までの苦労はなんだったのか。

やはり教えられたまま作業するんじゃだめよね。

権限を設定するステートメント

権限の許可 - GRANT

GRANT (オブジェクトの権限の許可) (Transact-SQL)

SSMSでいうところの許可のチェックボックスをチェックするのと同じ。

例: データベースTestDBのテーブルTestTableにおいて testuserのSELECTとUPDATEを許可する。

USE TestDB;
GRANT SELECT,UPDATE ON OBJECT::dbo.TestTable TO testuser;
GO

権限の取消 - REVOKE

REVOKE (オブジェクトの権限の取り消し) (Transact-SQL)

SSMSでいうところの許可もしくは拒否のチェックボックスのチェックを外す感じ。

例はGRANTの例のGRANTをREVOKEに変えるだけなので省略。

権限の拒否 - DENY

DENY (オブジェクトの権限の拒否) (Transact-SQL)

SSMSでいうところの拒否のチェックボックスをチェックするのと同じ。