素のWindowsで正規表現でgrepする (繰り返しのgrepが可能なPowerShellスクリプト)


概要

「サクラエディタ」およびサクラエディタが正規表現処理に利用するライブラリ "bregonig.dll" が、
諸般の事情により使用できないという方がいらっしゃるかも知れません。

そんな方は、Windowsに元から備わっている "FINDSTR" コマンド、または "PowerShell" を利用しましょう。
FINDSTRコマンド版、PowerShellコマンドレット版、それぞれバッチファイルに起こしました。
両者の差異は、grep処理部分にFINDSTRコマンドとPowerShellコマンドレットのどちらを用いるか、利用可能なオプションの種類です。管理者権限が無くてPowerShellスクリプトの実行が制限されている場合でも、引数を使用してPowerShellのコマンドを直接実行できるようです。PowerShellコマンドレット版が利用できる場合は、こちらの方が正規表現に癖が無いためオススメです。

grep処理部分だけでなく、完全にPowerShellで書いたスクリプトも開発しました。管理者権限が無くてPowerShellスクリプトの実行が制限されている場合でも、呼び出し時に実行ポリシーを指定することでスクリプトを実行できるようです。
PowerShellスクリプト版が利用できる場合は、こちらの方が正規表現に癖が無く、記号による構文エラーも起きにくいためオススメです。

環境

Windows 10, Windows 7

使用方法

PowerShellスクリプト版

  1. バッチファイル "Regrep.bat" を起動します。
  2. 検索対象のフォルダを問われるため、フォルダをドラッグするか、ディレクトリパスをコピペします。
  3. 検索対象のファイルを問われるため、ワイルドカードを使用して指定します。何も入力しない場合は、対象フォルダ内の全てのファイルから検索します。
    ワイルドカードとは、カードゲームのオールマイティカード(トランプで言えばジョーカー)に由来し、検索条件等において不特定の値を対象に含めたい場合に指定する記号のことです。'*' が一般的に使用され、その部分が如何なる文字種および長さの文字列であろうとも合致したとみなされます。例えば、"*.exe" とすると、拡張子が "exe" であるファイル全てが対象になります。
  4. オプションを指定します。's', 'u', 'z', 'L', 'r', 'i', 'v' の何れかを入力して、[Enter]キーを押下します。
    ※"srv" のように複数のオプションを同時指定可能です。
    • 's':サブディレクトリ以下のファイルも含める
    • 'u':UTF-8として処理
    • 'z':直前の検索を取り消す
    • 'L':grep結果にgrepしたファイル名を出力しない
    • 'r':検索文字列に正規表現を使用
    • 'i':検索文字列において大文字/小文字を区別
    • 'v':検索文字列に一致しない行を検索
  5. 検索文字列を入力します。
  6. 検索結果がテキストエディタで開き、合致した行数がプロンプト画面に表示されますので、確認します。
  7. 以降は手順4~6.を繰り返し、検索結果の絞り込みを行います。手順4.において "q" を入力すると終了します。

注意

  • grepの際の文字コードは、"Shift_JIS" をデフォルトにしています。オプションに "u" を指定すると "UTF-8" で処理されます。
  • PowerShellコマンドレットでは、FINDSTRコマンドと違い一般的な正規表現が利用可能です。
  • バッチファイルの構文解析の仕様上、検索文字列中の記号によっては正しく処理できない可能性がありますが、ご了承ください。
  • 検索実行毎に、その結果がバッチファイルと同じディレクトリにテキストファイルに出力されます。不要であれば削除して構いません。

FINDSTRコマンド版

  1. バッチファイル "FSgrep.bat" を起動します。
  2. 検索対象のフォルダを問われるため、フォルダをドラッグするか、ディレクトリパスをコピペします。
  3. 検索対象のファイルを問われるため、ワイルドカードを使用して指定します。何も入力しない場合は、対象フォルダ内の全てのファイルから検索します。
    ワイルドカードとは、カードゲームのオールマイティカード(トランプで言えばジョーカー)に由来し、検索条件等において不特定の値を対象に含めたい場合に指定する記号のことです。'*' が一般的に使用され、その部分が如何なる文字種および長さの文字列であろうとも合致したとみなされます。例えば、"*.exe" とすると、拡張子が "exe" であるファイル全てが対象になります。
  4. オプションを指定します。's', 'u', 'z', 'r', 'L', 'i', 'v' の何れかを入力して、[Enter]キーを押下します。
    ※"srv" のように複数のオプションを同時指定可能ですが、'r' と 'L' は排他的となります。
    • 's':サブディレクトリ以下のファイルも含める
    • 'u':UTF-8として処理
    • 'z':直前の検索を取り消す
    • 'r':検索文字列に正規表現を使用
    • 'L':検索文字列をリテラルとして扱う
    • 'i':検索文字列において大文字/小文字を区別
    • 'v':検索文字列に一致しない行を検索
  5. 検索文字列を入力します。
  6. 検索結果がテキストエディタで開き、合致した行数がプロンプト画面に表示されますので、確認します。
  7. 以降は手順4~6.を繰り返し、検索結果の絞り込みを行います。手順4.において "q" を入力すると終了します。

注意

  • コマンドプロンプトの仕様上、文字コードは "Shift_JIS" として扱われます。オプションに "u" を指定することで "UTF-8" で処理されますが、動作は保証しかねます。
  • FINDSTRとサクラエディタで利用される "bregonig.dll" とで、正規表現に差異があります。本バッチで利用可能な正規表現を次節にまとめました。
  • バッチファイルの構文解析の仕様上、検索文字列中の記号によっては正しく処理できない可能性があります。
  • 検索実行毎に、その結果がバッチファイルと同じディレクトリにテキストファイルに出力されます。不要であれば削除して構いません。

正規表現クイック リファレンス

利用可能な正規表現の一覧を、FINDSTRコマンドのヘルプから引用します。

  .            ワイルドカード: 任意の文字
  *            繰り返し: ゼロ個以上の直前の文字またはクラス
  ^            行位置: 行頭
  $            行位置: 行末
  [class]      文字クラス: セットの任意の 1 文字
  [^class]     逆クラス: セット以外の任意の 1 文字
  [x-y]        範囲: 指定した範囲の任意の文字
  \x           エスケープ: メタ文字 x のリテラル使用
  \<xyz        単語位置: 単語の先頭
  xyz\>        単語位置: 単語の終わり 

置換を行いたい場合

  • FINDSTRコマンドは、置換のような機能をサポートしていません。
  • PowerShellでgrep置換を行うスクリプトを作成することも可能と思われます。こちらの記事をどうぞ。
  • サクラエディタは使用できないけれどOfficeのWordが使用できる環境の方は、Wordがオススメです。対象のテキストファイルをWordで開く(またはWordの編集画面にテキストを貼り付ける)ことで、正規表現ライクな検索・置換操作が行えます。
    Word:ワイルドカードを使った検索と置換を極める
  • JScriptを使用すれば、本格的なGrep置換ができます。
    早速開発してみようかと思ったのですが、これで十分だなと言えるような良いページを見つけたので、それを紹介して終わりにします。
    なお、置換後は元のファイルが上書きされますので、バックアップした上での使用をオススメします。
    複数ファイルを一気に置換

ソースファイル

「マジック生成」するには、本ページ全体を選択してコピー後にB642FHT.batを起動して下さい。
その後、生成したZIPファイルを解凍して任意の場所へ配置して下さい。

興味ある方は、以下のコードをご覧ください。

コード

PowerShellスクリプト版

Regrep.bat
@ECHO OFF
cmd.exe /K PowerShell -ExecutionPolicy RemoteSigned -File %~dpn0.ps1 %1
Regrep.ps1
$Host.UI.RawUI.ForeGroundColor = "Green"

"############## Regrep.ps1 ##############"
"# grep script by PowerShell            #"
"#                                      #"
"#   1st release: 2019-06-21            #"
"#   Last update: 2019-07-29            #"
"#   Author: Y. Kosaka                  #"
"#   See the web for more information   #"
"#   https://qiita.com/x-ia             #"
"########################################"




$filenameScript = (Get-ChildItem $MyInvocation.MyCommand.Path).BaseName
$dirPathScript = Split-Path $MyInvocation.MyCommand.Path -Parent
$extLog = ".log"
$extOut = ".txt"
$qryFile = "*.*"
$cntLoop = 0
$Host.UI.RawUI.WindowTitle = $filenameScript


function SetDir ($arg) {
  :direxists while ($true){
    if($arg -ne $null){
      $qryDir = $arg
    }else{
      Write-Host "`r`nCurrent directory = $dirPathScript"
      Write-Host "`r`nEnter the folder path to grep."
      Write-Host "directory path:"
      $qryDir = Read-Host
    }
    if ($qryDir -eq "") {
      $qryDir = $dirPathScript
    }
    Write-Host "`r`nInput folder:"
    Write-Host $qryDir
    if (Test-Path $qryDir.Replace('"', '')) { break direxists }
    Write-Host "File not exists."
    $arg = $null
  }
  return $qryDir
}

function SetFile {
  Write-Host "`r`nEnter the filename to grep."
  Write-Host "Wildcards is available. ( Default value= *.* )"
  Write-Host "filename:"
  $qryFile = Read-Host
  return $qryFile
}

function SetFlag($cntLoop) {
  $flag=0
  Write-Host "`r`nEnter option below. (Multiple option possible)"
  if ($cntLoop -eq 0) {
    Write-Host "To include sub directories, enter 's'."
    Write-Host "To change the code page into "UTF-8", enter 'u'."
    Write-Host "Not to output filenames, enter 'L'."
  } else {
    Write-Host "To undo the previous , enter 'z'."
  }
  Write-Host "To use regular expressions, enter 'r'."
  Write-Host "To search w/o case-sensitive, enter 'i'."
  Write-Host "To search lines that do not match the condition, enter 'v'."
  Write-Host "To exit, enter 'q'"
  Write-Host "option:"
  $flag = Read-Host
  if ($cntLoop -ne 0) {
    $flag = $flag + "L"
  }
  return $flag
}

function UnDoCnt($flag, $cntLoop) {
  if ($flag -like "*z*") {
    $cntLoop -= 1
  }
  return $cntLoop
}

function UnDoFile($flag, $qryFile, $qryFilePrev) {
  if ($flag -like "*z*") {
    $qryFile = $qryFilePrev
    Write-Host "Undo ($qryFilePrev)"
  }
  return $qryFile
}

function SetOptSD($flag, $cntLoop) {
  if ($cntLoop -eq 0) {
    if ($flag -like "*s*") {
      $optSD = " -recurse"
      Write-Host "Including sub directories: ON"
    } else {
      $optSD = ""
      Write-Host "Including sub directories: OFF"
    }
  } else {
    $optCP = ""
  }
  return $optSD
}

function SetOptCP($flag, $cntLoop) {
  if ($cntLoop -eq 0) {
    if ($flag -like "*u*") {
      $optCP = " -Encoding utf8"
      Write-Host "Code page: UTF-8"
    } else {
      $optCP = " -Encoding default"
      Write-Host "Code page: Shift_JIS"
    }
  } else {
    $optCP = ""
  }
  return $optCP
}

function SetOptRE($flag) {
  if ($flag -like "*r*") {
    $optRE = ""
    Write-Host "Regular expression: ON"
  } else {
    $optRE = " -SimpleMatch"
    Write-Host "Regular expression: OFF"
  }
  return $optRE
}

function SetOptCS($flag) {
  if ($flag -like "*i*") {
    $optCS = ""
    Write-Host "Case-sensitive: OFF"
  } else {
    $optCS = " -CaseSensitive"
    Write-Host "Case-sensitive: ON"
  }
  return $optCS
}

function SetOptInv($flag) {
  if ($flag -like "*v*") {
    $optInv = " -NotMatch"
    Write-Host "Not match the condition: ON"
  } else {
    $optInv = ""
  }
  return $optInv
}

function SetOut {
  $dateNow = (Get-Date).ToString("yyyyMMdd")
  $timeNow = (Get-Date).ToString("HHmmss")
  $fileResult = $dirPathScript + "\" + $filenameScript + "_" + $dateNow + "-" + $timeNow + $extOut
  return $fileResult
}

function SetLog {
  $fileLog = $dirPathScript + "\" + $filenameScript + $extLog
  return $fileLog
}

function SetKey() {
  :keyinput while ($true){
    Write-Host "`r`nEnter the keyword to grep."
    $qryKey = $null
    Write-Host "keyword:"
    $qryKey = Read-Host
    Write-Host $qryKey
    if (($qryKey -ne $null) -And ("$qryKey" -ne "")) { break keyinput }
    Write-Host "Keyword is empty."
  }
  return $qryKey
}

function Grep($flag, $qryKey, $optSD, $qryFile, $optCP, $optRE, $optCS, $optInv, $fileResult) {
  if ($flag -like "*L*") {
    $optLine = "| Select Line"
  } else {
    $optLine = ""
  }
  $cmdGrep = "Select-String `"$qryKey`" (dir $optSD $qryFile) $optCP $optRE $optCS $optInv $optLine | `
  Select-Object Path,LineNumber,Line | ConvertTo-CSV -Delimiter `"`t`" -NoType | `
  % { `$_ -Replace `'`"`',`"`"} | Out-File -FilePath $fileResult -width 1000"
  Invoke-Expression $cmdGrep
}

function GetResult($fileResult, $cntLoop) {
  $numLine = (Select-String ":" $fileResult).Count
  Write-Host "`r`n $numLine lines match."
  $Host.UI.RawUI.WindowTitle = $filenameScript + " " + $cntLoop +"filts " + $numLine + "lines"
  return $numLine
}

function PutLog($filenameScript, $numLine, $qryDir, $qryFile, $flag, $qryKey, $fileLog) {
  $timeStampNow = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss.fff")
  "$timeStampNow`t$filenameScript`t$numLine`t$qryDir`t$qryFile`t$flag`t$qryKey" | `
  Out-File $fileLog -Encoding utf8 -Append
}

function EoF {
  Write-Host "Terminated by user.`r`nPlease any key."
  Write-Host "`r`n"
  $host.UI.RawUI.ReadKey()
  exit
}


$qryDir = $null
$qryDir = SetDir $Args[0]
cd $qryDir

$qryFile = SetFile
:regrep while ($true) {
  $flag = SetFlag $cntLoop
  Write-Host "`r`n"
  if ($flag -like "*q*") {EoF}
  $cntLoop = UnDoCnt $flag $cntLoop
  $qryFile = UnDoFile $flag $qryFile $qryFilePrev
  $optSD = SetOptSD $flag $cntLoop
  $optCP = SetOptCP $flag $cntLoop
  $optRE = SetOptRE $flag
  $optCS = SetOptCS $flag
  $optInv = SetOptInv $flag
  $qryKey = SetKey
  $fileResult = SetOut
  $fileLog = SetLog
  Grep $flag $qryKey $optSD $qryFile $optCP $optRE $optCS $optInv $fileResult
  $cntLoop += 1
  $numLine = GetResult $fileResult $cntLoop
  & $fileResult
  PutLog $filenameScript $numLine $qryDir $qryFile $flag $qryKey $fileLog
  $qryFilePrev=$qryFile
  $qryFile=$fileResult
}

FINDSTRコマンド版

FSgrep.bat
@ECHO OFF
COLOR 0A

ECHO ############## FSgrep.bat ##############
ECHO # grep batch by using findstr commamd  #
ECHO #                                      #
ECHO #   Last update: 2019-06-15            #
ECHO #   Author: Y. Kosaka                  #
ECHO #   See the web for more information   #
ECHO #   https://qiita.com/x-ia             #
ECHO ########################################

SET tScr=%~n0
SET dScr=%~dp0
SET extOut=.txt
SET extLog=.log
SET tFile=*.*
SET nLoop=0

SET pFolder=
CALL :dInput %1
IF %pFolder% EQU "" GOTO eof
IF NOT DEFINED pFolder GOTO eof
IF NOT EXIST %pFolder% (
  ECHO File not exists.
  GOTO loop
)

CD /D %pFolder%
CALL :fInput

:loop
CALL :setflag
IF "%flag%" EQU "q" GOTO eof
CALL :setopt
CALL :setkey
CALL :setout
CALL :grep
CALL :getresult
CALL :putlog
ECHO.
SET tFilePrev=%tFile%
SET tFile=%fResult%
GOTO loop

PAUSE
EXIT /B


:dInput
ECHO.
IF "%1" NEQ "" (
  SET pFolder=%1
) ELSE (
  ECHO Current directory = %~dp0
  ECHO.
  ECHO Enter the folder path to grep.
  SET /P pFolder=folderpath= 
)

SET pFolder="%pFolder%"
SET pFolder="%pFolder:"=%"
ECHO Input folder:
ECHO %pFolder%

IF %pFolder% EQU "" EXIT /B
ECHO "%pFolder%" | FIND /I "\" >NUL
IF ERRORLEVEL 1 SET pFolder=%~dp0%pFolder%
SET pFolder="%pFolder:"=%"
EXIT /B


:fInput
ECHO.
ECHO Enter the file to grep.
ECHO Wildcards is available. ( Default value= *.* )
SET /P tFile=file= 
EXIT /B


:setflag
ECHO.
SET flag=0
ECHO Enter option below. (Multiple option possible)
IF %nLoop% EQU 0 (
  ECHO To include sub directories, enter "s".
  ECHO To change the code page into "UTF-8", enter "u".
) ELSE (
  ECHO To undo the previous , enter "z".
)
ECHO To use regular expressions, enter "r".
ECHO To construe as leteral, enter "L".
ECHO To search w/o case-sensitive, enter "i".
ECHO To search lines that do not match the condition, enter "v".
ECHO To exit, enter "q"
SET /P flag=Choice= 
ECHO.
:xclflag
ECHO "%flag%" | FIND /I "r" >NUL
IF NOT ERRORLEVEL 1 (
  ECHO "%flag%" | FIND /I "l" >NUL
  IF NOT ERRORLEVEL 1 (
    ECHO Entered option is invalid, because "r" ^& "L" are exclusive.
    GOTO setflag
  )
)
EXIT /B


:setopt
IF %nLoop% EQU 0 (
  ECHO "%flag%" | FIND /I "s" >NUL
  IF NOT ERRORLEVEL 1 (
    SET optSD=/S 
    ECHO Including sub directories: ON
  ) ELSE (
    SET optSD=
    ECHO Including sub directories: OFF
  )
  ECHO "%flag%" | FIND /I "u" >NUL
  IF NOT ERRORLEVEL 1 (
    CHCP 65001
    ECHO Code page: UTF-8
  )
)

ECHO "%flag%" | FIND /I "z" >NUL
IF NOT ERRORLEVEL 1 (
  SET tFile=%tFilePrev% 
  ECHO Undo (%tFilePrev%^)
)

ECHO "%flag%" | FIND /I "r" >NUL
IF NOT ERRORLEVEL 1 (
  SET optRE=/R 
  ECHO Regular expression: ON
) ELSE (
  SET optRE=
  ECHO Regular expression: OFF
)

ECHO "%flag%" | FIND /I "l" >NUL
IF NOT ERRORLEVEL 1 (
  SET optL=/L 
  ECHO Literal: ON
) ELSE (
  SET optL=
  ECHO Literal: OFF
)

ECHO "%flag%" | FIND /I "i" >NUL
IF NOT ERRORLEVEL 1 (
  SET optCS=/I 
  ECHO Case-sensitive: OFF
) ELSE (
  SET optCS=
  ECHO Case-sensitive: ON
)

ECHO "%flag%" | FIND /I "v" >NUL
IF NOT ERRORLEVEL 1 (
  SET optInv=/V 
  ECHO Not match the condition: ON
) ELSE (
  SET optInv=
  ECHO Not match the condition: ON
)

ECHO.
EXIT /B


:setout
SET dateNow=%date:~0,4%%date:~5,2%%date:~8,2%
SET timeNow=%time:~0,2%%time:~3,2%%time:~6,2%
SET timeNow=%timeNow: =0%
SET fResult=%~dp0%tScr%-result_%dateNow%_%timeNow%%extOut%
SET fLog=%~dp0%tScr%%extLog%
EXIT /B


:setkey
ECHO Enter the keyword to grep.

ECHO "%flag%" | FIND /I "r" >NUL
IF NOT ERRORLEVEL 1 (
  ECHO  .   Wildcard: any character
  ECHO  *   Repeat: zero or more occurrences of the previous character or class
  ECHO  \^  Line position: beginning of the line
  ECHO  $   Line position: end of the line
  ECHO  [class]     Character class: any one character in a set
  ECHO  [^class]    Inverse class: any one character not in a set
  ECHO  [x-y]   Range: any characters within the specified range
  ECHO  \x  Escape: literal use of a metacharacter x
  ECHO  \^<string   Word position: beginning of the word
  ECHO  string^>    Word position: end of the word
  ECHO.
)

SET tKey=
SET /P tKey=keyword= 
IF NOT DEFINED tKey GOTO setkey
IF "%tKey: =%" EQU "" GOTO setkey
SET tKey="%tKey:"=\^"%"
EXIT /B


:grep
FINDSTR %optSD%%optRE%%optCS%%optL%%optInv% %tKey% %tFile%>>"%fResult%"
SET /A nLoop+=1
EXIT /B


:getresult
FOR /F %%I IN ('FIND /C ":" ^< "%fResult%"') DO SET nLine=%%I
ECHO %nLine% lines match.
TITLE %tScr% %nLoop%filt %nLine%lines
REM START /MIN CALL "%fResult%"
START "" "%fResult%"
EXIT /B


:putlog
REM ECHO %date% %time%  %nLine% %pFolder%   %tFile% %flag%  %tKey%
ECHO %date% %time%  %tScr%  %nLine% %pFolder%   %tFile% %flag%  %tKey%>>%fLog%
EXIT /B


:eof
ECHO Terminated. 
PAUSE
REM ENDLOCAL
EXIT /B

バイナリ (Base64 encoding)

PowerShell_script_edition
Regrep.zip
---
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
---
FINDSTR_command_edition
FSgrep.zip
---
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
AQABADgAAADgBgAAAAA=
---