はじめに
会社では運用チームに属していて、各県にある30近い工場の運用作業を行っています。
システムの導入作業があると工場ごとに設定ファイルを変更して導入しており、現在は設定ファイルの編集は手作業で行っている。
その為、「【WSH】設定ファイル(xml,ini)の編集ツール」(ReplaceConfig.vbs)を作成することで効率化することが出来た。しかし、製品名を変更したいという要望もあり、もう一工夫必要になった。
ClickOnceの発行物を「MageUI.exe」を使うことで、製品名やバージョンやStart Locationなどの設定を変更することが出来ますが、製品名を変更しても「publish.htm」ファイルまでは更新されません。
製品名が書かれている「.application」ファイルの中身はXMLファイルであるため、編集ツール(ReplaceConfig.vbs)を使うことで変更できたが、「publish.htm」ファイルはHTMLであり特定できるキーが無いため、別途、正規表現で置換するツールを作成することにしました。
通常使われる文字コードや改行コードには対応しております。
2024/10/04追記
コメント付きのjsonファイルを置換したら、日本語コメントが文字化けする不具合を修正
2016/11/16追記
置換対象の値を取得できるといいなと思ったので、機能を追加しました。
使用方法
「ReplaceRegex.vbs」は引数を4つ指定できます。
ReplaceRegex.vbs FileName Search Value [CharSet]
-
FileName
置換対象ファイルを指定します。 -
Search
検索文字列(正規表現対応)を指定します。例 /ab+c/gi
正規表現を使う場合はJavascriptの正規表現の指定方法と同じく「/正規表現文字列/オプション」となります。オプションは2種類で「g」オプションはグローバルマッチ、「i」オプションは英大文字と小文字を無視です。
正規表現を書く際に「<」や「>」や「|」などコマンドラインに使う文字や空白や含むや文字列は2重引用符で囲んでください。
特殊文字「"」や「%」などを値として使う場合は、「#(16進数)」としてください。2重引用符の「"」は「#22」となります。 -
Value
変更する値(正規表現対応)を指定します。例 \$1-\$2-\$3
検索文字列が正規表現だった場合に後方参照など正規表現用の置換文字列を指定できます。
また、「<」や「>」や「|」などコマンドラインに使う文字や空白や含むや文字列は2重引用符で囲んでください。
特殊文字「"」や「%」などを値として使う場合は、「#(16進数)」としてください。2重引用符の「"」は「#22」となります。
固定文字列「GETVALUE」にすると、置換ではなく置換対象の値を取得します。 -
CharSet
BOM無しの場合の文字コードをセットします。不要な場合は指定する必要はありません。指定しない場合は「Shift-JIS」となります。
※ファイル内の5行目以内に「encoding」や「charset」があれば指定文字コードを優先します。
使用例
ClickOnceの「publish.htm」ファイルの製品名が「外注受払いシステム(イントラネットワーク使用)」、タイトルが「外注受払いシステム(イントラ側ネットワーク使用)」と「側」が付いているという違いがあります。この括弧内を「工程用」に変更します。
ReplaceRegex publish.htm /イントラ.?ネットワーク使用/ 工程用
rem データベース接続情報のIPアドレスを変更
ReplaceRegex "database.json" /Server=(.*?);/ 192.168.64.14; UTF-8
ReplaceRegex publish.htm /(\()(イントラ.?ネットワーク使用)(\))/ $1工程用$3
「イントラ側ネットワーク使用」で統一されていた場合
ReplaceRegex publish.htm イントラ側ネットワーク使用 工程用
値の取得
最初に見つけた「イントラ側ネットワーク使用」を取得します。
for /f "usebackq tokens=*" %%i IN (`call cscript //nologo ReplaceRegex.vbs publish.htm "/イントラ.?ネットワーク使用/" GETVALUE`) DO @set result=%%i
echo %result%
正規表現が使えるため括弧でグループ化するとカンマ区切りにして取得します。
例 (,イントラ側ネットワーク使用,)
※バッチでは括弧はエスケープされてしまうため、2重引用符で囲む必要があります。
for /f "usebackq tokens=*" %%i IN (`call cscript //nologo ReplaceRegex.vbs publish.htm "/(\()(イントラ.?ネットワーク使用)(\))/" GETVALUE`) DO @set result=%%i
echo %result%
ソースリスト
'--------------------------------------
' 引数1: 変更するファイル
' 引数2: 検索文字列(正規表現)
' 引数3: 変更文字列(正規表現)
' 引数4: 文字コード(BOM以外)
'--------------------------------------
Option Explicit
'On Error Resume Next
Const ForReading = 1, ForWriting = 2, ForAppEnding = 8
Const adTypeBinary = 1, adTypeText = 2
Const adReadAll = -1, adReadLine = -2
Const GET_CONFIG = "GETVALUE"
Dim objArgs, fso
Dim pathName, searchPattern, valuePattern, charSet, strLine
'コマンドライン引数取得
Set objArgs = WScript.Arguments
'対象パス名取得
If objArgs.Count >= 1 Then
pathName = objArgs(0)
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(pathName) = False Then
WScript.Echo pathName & " ファイルが存在しません"
WScript.Quit(1)
End If
Set fso = Nothing
Else
WScript.Echo "対象パス名が見つかりません"
WScript.Quit(1)
End If
'検索文字列(正規表現)取得
If objArgs.Count >= 2 Then
searchPattern = objArgs(1)
Else
WScript.Echo "検索文字列が見つかりません"
WScript.Quit(2)
End If
'変更文字列(正規表現)取得
If objArgs.count >= 3 then
valuePattern = objArgs(2)
Else
WScript.Echo "変更文字列が見つかりません"
WScript.Quit(3)
End If
'文字コード(BOM以外)
charSet = "SJIS"
If objArgs.count >= 4 then
charSet = UCase(objArgs(3))
End If
'ファイル情報を取得する
Dim param, result
Set param = CreateObject("Scripting.Dictionary")
Call GetInfo(pathName, charSet)
'正規表現対応の置換処理
result = Main(pathName, searchPattern, valuePattern)
Set param = Nothing
WScript.Quit(result)
'------------------------------------------------------------------------
' 正規表現対応の置換処理
' pathName: 対象パス名
' searchPattern: 検索文字列(正規表現)
' valuePattern: 変更文字列(正規表現)
'------------------------------------------------------------------------
Function Main(pathName, searchPattern, valuePattern)
Dim reg, matches, match, stream_r, stream_pre, stream_w
Dim bin, newtxt, line, lineSave, i, idx, count, regPattern
Dim regexFlag, exitFlag, lfType, allLine, aryLine, result
Main = 0
Set stream_r = CreateObject("ADODB.Stream")
Set stream_pre = CreateObject("ADODB.Stream")
Set stream_w = CreateObject("ADODB.Stream")
Set reg = CreateObject("VBScript.RegExp")
reg.Global = True
reg.IgnoreCase = True
'現行の文字コードで読み込む
stream_r.Open
stream_r.CharSet = param("CharSet")
stream_r.LoadFromFile pathName
allLine = stream_r.ReadText(adReadAll)
stream_r.Close
'改行コード
lfType = ""
Select Case param("LfType")
Case "CrLf": lfType = vbCrLf
Case "Lf": lfType = vbLf
Case "Cr": lfType = vbCr
End Select
'改行コードで分割
aryLine = Split(allLine, lfType)
'検索文字列の#(16進数)の文字変換
searchPattern = ReplaceChr(searchPattern)
'変更文字列の#(16進数)の文字変換
valuePattern = ReplaceChr(valuePattern)
'検索文字列が正規表現かを確認
regexFlag = False
reg.Pattern = "\/(.*)\/([gi]*)$"
reg.Global = False
reg.IgnoreCase = False
Set matches = reg.Execute(searchPattern)
regexFlag = (matches.Count > 0)
For Each match In matches
regPattern = match.SubMatches(0)
Select case match.SubMatches(1)
Case "g"
reg.Global = True
Case "i"
reg.IgnoreCase = True
Case "gi","ig"
reg.Global = True
reg.IgnoreCase = True
End Select
Next
Set matches = Nothing
i = 0
count = 0
exitFlag = False
For idx = 0 To UBound(aryLine)
line = aryLine(idx)
If regexFlag = True Then
'正規表現による置換
reg.Pattern = regPattern
If reg.Test(line) Then
'置換
result = GetValue(reg, valuePattern, line)
line = reg.Replace(line, valuePattern)
exitFlag = True
End If
Else
'通常の置換
result = GetValue(reg, valuePattern, line)
lineSave = line
line = Replace(line, searchPattern, valuePattern)
If line <> lineSave Then exitFlag = True
End If
'読み込んだ行を再セット
aryLine(idx) = line
Next
If exitFlag = True Then
If UCase(valuePattern) = GET_CONFIG Then
'値を返す
WScript.Echo result
Else
'改行を付加
newtxt = Join(aryLine, lfType)
'ファイル保存
If param("CharSet") = "UTF-8" And param("IsBOM") = False Then
'BOM無し
stream_pre.Type = adTypeText
stream_pre.CharSet = param("CharSet")
stream_pre.Open
stream_pre.WriteText newtxt
stream_pre.Position = 0
stream_pre.Type = adTypeBinary
stream_pre.Position = 3
bin = stream_pre.Read()
stream_pre.Close()
stream_w.Type = adTypeBinary
stream_w.Open()
stream_w.Write(bin)
stream_w.SaveToFile pathName, ForWriting
stream_w.Close()
Else
stream_w.Open()
stream_w.Charset = param("CharSet")
stream_w.WriteText newtxt
stream_w.SaveToFile pathName, ForWriting
stream_w.Close()
End If
End If
Else
WScript.Echo "検索文字列が見つかりませんでした。"
Main = 9
End If
Set reg = Nothing
End Function
'------------------------------------------------------------------------
'#(16進数)の文字変換
'------------------------------------------------------------------------
Function ReplaceChr(target)
Dim reg, matches, match, hexvalue
Set reg = CreateObject("VBScript.RegExp")
reg.Global = True
reg.IgnoreCase = True
reg.Pattern = "#([0-9A-Fa-f]{2})"
Set matches = reg.Execute(target)
For Each match In matches
hexvalue = match.SubMatches(0)
target = Replace(target, "#" & hexvalue, Chr(CInt("&H" & hexvalue)))
Next
Set matches = Nothing
ReplaceChr = target
Set reg = Nothing
End Function
'------------------------------------------------------------------------
'ファイルの文字コード等の情報を取得する
'------------------------------------------------------------------------
Function GetInfo(pathName, charSet)
Dim stream, reg, match, matches, bytCode, allLine, line, endLine, lfType, aryLine
Dim i, charcode
Const FindMaxLine = 5 'BOM無し時の文字コード探索行
GetInfo = False
'初期値セット
param.Add "CharSet", charSet '文字コード
param.Add "IsBOM", False 'BOM有無
param.Add "LfType", "CrLf" '改行コード
param.Add "IsEndLf", False '末尾改行有無
'ファイルをオープン
Set stream = CreateObject("ADODB.Stream")
'BOMチェック用に先頭行を読み込む
stream.Type = adTypeBinary
stream.Open
stream.LoadFromFile pathName
bytCode = stream.Read
stream.Close
Call CheckBOM(bytCode)
'文字コードを自動判定で読み込み
stream.Type = adTypeText
stream.CharSet = "_autodetect_all"
If charSet <> "" Then stream.CharSet = charSet
stream.Open
stream.LoadFromFile pathName
allLine = stream.ReadText(adReadAll)
stream.Close
'改行コード判定(100文字で判断)
line = Left(allLine, 100)
If Instr(line, vbCrLf) <> 0 Then
lfType = "CrLf"
ElseIf Instr(line, vbLf) <> 0 Then
lfType = "Lf"
ElseIf Instr(line, vbCr) <> 0 Then
lfType = "Cr"
End If
param("LfType") = lfType
'改行コードで分割
Select Case param("LfType")
Case "CrLf": lfType = vbCrLf
Case "Lf": lfType = vbLf
Case "Cr": lfType = vbCr
End Select
aryLine = Split(allLine, lfType)
'BOM無しなら、encodingとcharsetの文字コード参照
If param("IsBOM") = False Then
Set reg = CreateObject("VBScript.RegExp")
'encodingまたはcharsetの値を文字コードにセット
reg.Pattern = "(?:encoding|charset).*?=(?:['""](.*?)['""]|(.*))"
reg.IgnoreCase = True
For i = 0 To FindMaxLine - 1
Set matches = reg.Execute(aryLine(i))
For Each match In matches
charcode = UCase(match.SubMatches(0) & match.SubMatches(1))
param("CharSet") = charcode
Next
If charcode > "" Or i >= UBound(aryLine) Then Exit For
Next
Set matches = Nothing
Set reg = Nothing
End If
'最終行の改行コード有無
If aryLine(UBound(aryLine)) = "" Then
param("IsEndLf") = True
End If
GetInfo = True
End Function
'------------------------------------------------------------------------
'BOMチェックの情報を取得する
'------------------------------------------------------------------------
Function CheckBOM(str)
Dim bytes(2), result
bytes(0) = AscB(MidB(str, 1, 1))
bytes(1) = AscB(MidB(str, 2, 1))
bytes(2) = AscB(MidB(str, 3, 1))
If LenB(str) > 1 Then
If bytes(0) >= &HFE And bytes(1) >= &HFE Then
result = "unicode"
If bytes(0) = &HFF And bytes(1) = &HFE Then
result = "unicodeFFFE"
End If
Else If bytes(0) = &HEF And bytes(1) = &HBB And bytes(2) = &HBF Then
result = "UTF-8"
End If
End If
End If
If result <> "" Then
param("CharSet") = result
param("IsBOM") = True
End If
CheckBOM = result
End Function
'------------------------------------------------------------------------
'現在の設定値を取得する
'------------------------------------------------------------------------
Function GetValue(reg, valueString, target)
Dim matches, match, comma, i
GetValue = ""
If UCase(valueString) = GET_CONFIG Then
Set matches = reg.Execute(target)
For Each match In matches
If match.SubMatches.Count > 0 Then
comma = ""
For i=0 To match.SubMatches.Count - 1
GetValue = GetValue & comma & match.SubMatches(i)
comma = ","
Next
Else
GetValue = match.Value
Exit For
End If
Next
End If
End Function
ライセンスっぽいこと
- コード改変や配布は自由です。
- このツールによる義務/責任を何ら負いません。
最後に
「コマンドライン 正規表現 置換」で検索すると、「mfind」や「sakuraエディタのコマンドラインオプション」ぐらいしか見つからないのであまり需要はないかな。
プログラムは不具合があれば随時修正していきます。
このツールがお役に立てれば幸いです。