(PS) Script to Split Reg/InI file into multiple files

Discussion in 'Scripting' started by KNARZ, Jan 18, 2020.

  1. KNARZ

    KNARZ MDL Addicted

    Oct 9, 2012
    895
    482
    30
    Hey,
    unfortunatly I'm getting on my limits with Batch and I should have learned PowerShell long time ago but I'm not confident enough with PowerShell. So it would be nice if someone can help me starting some problem I have.

    I have several Reg files (Reg are similar InI files) which contain many different registry keys.
    I want to split every key with Value into a single file.

    example Source:
    regfile-blob:
    Code:
    [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System]
    "WallpaperStyle"="5"
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\DriverSearching]
    "DontSearchCD"=dword:00000001
    "DontSearchFloppies"=dword:00000001
    "DontSearchWindowsUpdate"=dword:00000001
    
    I want to export this one file blob into a folder structure (like the reg key) and many flat files.
    So based on the example a File like/in should be generated: C:\User\Software\Policies\Microsoft\Windows\DriverSearching.reg
    DriverSearching than contains:.
    Code:
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\DriverSearching]
    "DontSearchCD"=dword:00000001
    "DontSearchFloppies"=dword:00000001
    "DontSearchWindowsUpdate"=dword:00000001
    
    If file exisit (because auf mutiple files can contain same content) a number should be added (DriverSearching1.reg)
    C:\User\Software\Microsoft\Windows\CurrentVersion\Policies\System.reg
    Commented (;) lines should NOT be skipped and just added to the section / regkey

    So to clearify: Every full regkey should be one single file and can contain mutiple value and data, everything after [KEY] should just be added into the file (up till the next key)
    It may also shloud be possible to skip/ignore the prefix of HKLM / HKCU
     
  2. Thomas Dubreuil

    Thomas Dubreuil MDL Senior Member

    Aug 29, 2017
    363
    620
    10
    #2 Thomas Dubreuil, Jan 19, 2020
    Last edited: Jan 19, 2020
    something like this should get you started, I added comments to make it easier to modify...
    Code:
    @echo off
    
    set "filetoparse=%~dpnx1"
    setlocal EnableDelayedExpansion
    REM parse registry file adding line number
        for /f "tokens=1* delims=:" %%a IN ('findstr /n "^" "%filetoparse%"') do (
            set "linenumber=%%a"
            set "regkey=%%b"
            if "!regkey:~0,3!"=="[HK" ( call :loopprocess )
        )
    pause
    exit /b
    
    :loopprocess
        set "var1=!regkey!"
        set "var2=!regkey!"
        set "tok=0"
    REM count tokens
    :loopprocess2
        for /F "tokens=1* delims=[\]" %%G in ( "!var1!" ) do (
          set /A "tok+=1"
          set var1=%%H
          goto loopprocess2
        )
    REM echo The string contains !tok! tokens.
    REM get last token name
        for /F "tokens=%tok% delims=[\]" %%J in ( "!var2!" ) do ( set "last=%%J" )
    REM create reg file with last token name
        echo Windows Registry Editor Version 5.00>"%~dp0%last%.reg"
        echo: >>"%~dp0%last%.reg"
        echo !regkey!>>"%~dp0%last%.reg"
    REM parse next lines
    :loopprocess3
        set /a "linenumber+=1"
        for /f "tokens=1* delims=:" %%S IN ('findstr /n "^" "%filetoparse%"') do (
            set "newlinenumber=%%S"
            set "linevalue=%%T"
            if "!newlinenumber!"=="!linenumber!" (
    REM exit loop if next line is a registry key
                if "!linevalue:~0,3!"=="[HK" ( goto :eof )
    REM print next line if line exist
                if defined linevalue (
                    echo !linevalue!>>"%~dp0%last%.reg"
                    goto :loopprocess3
        )))
    goto :eof
    for HKLM not sure what you meant...You can do string replacement before echoing the key, something like this
    REM create reg file with last token name
    set "regkey=!regkey:HKEY_CURRENT_USER=HKCU!"
    set "regkey=!regkey:HKEY_LOCAL_MACHINE=HKLM!"
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  3. KNARZ

    KNARZ MDL Addicted

    Oct 9, 2012
    895
    482
    30
    Thank you VERY much for the starting! Really appricated. I thought it would be easier in PS than Batch but I take what I get as this is only for internal Tools..
    I modded and tweaked the script in some areas (we have differnt coding style ^^ - I like to work will call and you seem a pro with recursiv loops)

    I figured 2 problems.
    1) Delete with [-HK is not yet supported (but I can solve this on my own (just skip loop 3)
    2) It works but I don't like that in loop3 every line starts a new instance of findstr. - Do you have some other solution in mind?

    (IncrementFileName isn't working yet.. it's more like a ToDo currently)

    I would have all done it very different ^^ but I like the approach!

    Code:
    @echo off
    setlocal EnableDelayedExpansion
    
    set "filetoparse=%~dpnx1"
    REM parse registry file adding line number
    for /f "tokens=1* delims=:" %%a IN ('findstr.exe /n "^" "%filetoparse%"') do (
        set "linenumber=%%a"
        set "regfileline=%%b"
        if "!regfileline:~0,3!"=="[HK"    (call :loopprocess %%a %%b)
        if "!regfileline:~0,4!"=="[-HK"    (call :delete)
    )
    exit /b
    
    :loopprocess
        set "regkey=!regfileline!"
        set "tok=0"
    REM count tokens
    :loopprocess2
    for /F "tokens=1* delims=[\]" %%G in ( "!regkey!" ) do (
        if !tok! EQU 0 (set reg_path=%%H)
        REM echo !tok! %%G
        set /A "tok+=1"
        set regkey=%%H
        set last=%%G
        goto loopprocess2
    )
    call :CleanUpRegPath
    call :FolderCheckAndCreate
    call :CreateFile
    call :loopprocess3
    goto :EOF
    
    
    REM parse next lines
    :loopprocess3
    set /a "linenumber+=1"
    for /f "tokens=1* delims=:" %%S IN ('findstr.exe /n "^" "%filetoparse%"') do (
        set "newlinenumber=%%S"
        set "linevalue=%%T"
        if "!newlinenumber!"=="!linenumber!" (
            REM exit loop if next line is a registry key
            if "!linevalue:~0,3!"=="[HK"    (goto :EOF)
            if "!linevalue:~0,4!"=="[-HK"    (goto :EOF)
            REM print next line if line exist
            if defined linevalue (
                echo !linevalue!>>"%reg_export%"
                goto :loopprocess3
            )
        )
    )
    goto :eof
    
    :CleanUpRegPath
    set "reg_path=!reg_path:[=!"
    set "reg_path=!reg_path:]=!"
    set "reg_path=!reg_path:\%last%=!"
    goto :EOF
    
    :FolderCheckAndCreate
    set "RegFullPath=%~dp0!reg_path!"
    if not exist "%RegFullPath%" (mkdir "%RegFullPath%")
    goto :EOF
    
    :CreateFile
    set "reg_export=%RegFullPath%\%last%.reg"
    if exist "%reg_export%" (call :IncrementFileName "%last%")
    echo Windows Registry Editor Version 5.00>"!reg_export!"
    echo: >>"!reg_export!"
    echo !regfileline!>>"!reg_export!"
    goto :EOF
    
    :IncrementFileName
    for /l %%i in (1 1 99) do (if NOT exist "%RegFullPath%\%last%_%%i.reg" (set "last=%last%_%%i" && goto :EOF))
    goto :EOF
     
  4. KNARZ

    KNARZ MDL Addicted

    Oct 9, 2012
    895
    482
    30
    I am working on the problem 2 with all the findstr. instances. I pretty close but I have another problem I never got it right to solve.
    If I want to expand a variable which is part of a !! variable I don't know how the expand it.

    I need to resolve (get the data of): %section%_Line_!%section%_count! which "might result" in !%section%_Line_!%section%_count!!
    But double !!var!! does not work that way. I know this but never found a solution to it - any Idea?

    Current Version:
    Code:
    @echo off
    setlocal EnableDelayedExpansion
    set "filetoparse=%~dpnx1"
    
    REM parse registry file adding line number
    for /f "tokens=1* delims=:" %%a IN ('findstr.exe /n "^" "%filetoparse%"') do (
        set "linenumber=%%a"
        set "regfileline=%%b"
        if "!regfileline:~0,3!"=="[HK"    (call :loopprocess %%a %%b)
        if "!regfileline:~0,4!"=="[-HK"    (call :delete)
    )
    exit /b
    
    :loopprocess
        set "regkey=!regfileline!"
        set "tok=0"
    REM count tokens
    :loopprocess2
    for /F "tokens=1* delims=[\]" %%G in ( "!regkey!" ) do (
        if !tok! EQU 0 (set reg_path=%%H)
        REM echo !tok! %%G
        set /A "tok+=1"
        set regkey=%%H
        set last=%%G
        set section=%%G
        goto loopprocess2
    )
    call :CleanUpRegPath
    call :FolderCheckAndCreate
    call :CreateFile
    call :InitLoop
    call :ValueLoop
    goto :EOF
    
    :InitLoop
    set /a "%section%_Count=0"
    for /f "tokens=1* delims=:" %%S IN ('findstr.exe /n "^" "%filetoparse%"') do (
        set /a "%section%_Count+=1"
        REM echo Count: !%last%_count!
        set "%section%_Line_!%section%_count!=%%S"
        set "%section%_LineValue_!%section%_count!=%%T"
    )
    goto :EOF
    
    :ValueLoop
    set /a "linenumber+=1"
    echo Maxcount: !%section%_Count!
    echo "!%section%_Line_!%section%_count!!"
    echo  "%section%_Line_!%section%_count!"=="!linenumber!"
    for /l %%i in (1 1 !%section%_Count!) do (
        if "%section%_Line_!%section%_count!"=="!linenumber!" (
            REM exit loop if next line is a registry key
            if "!section%_LineValue_!%section%_count:~0,3!"=="[HK"    (goto :EOF)
            if "!section%_LineValue_!%section%_count:~0,4!"=="[-HK"    (goto :EOF)
            REM print next line if line exist
            if defined %section%_LineValue_!%section%_count! (
                REM echo !%Last%_linevalue!
                echo !%section%_LineValue_!%section%_count!!>>"%reg_export%"
                goto :ValueLoop
            )
    )
    goto :EOF
    
    :CleanUpRegPath
    set "reg_path=!reg_path:[=!"
    set "reg_path=!reg_path:]=!"
    set "reg_path=!reg_path:\%last%=!"
    goto :EOF
    
    :FolderCheckAndCreate
    set "RegFullPath=%~dp0!reg_path!"
    if not exist "%RegFullPath%" (mkdir "%RegFullPath%")
    goto :EOF
    
    :CreateFile
    set "reg_export=%RegFullPath%\%last%.reg"
    REM if exist "%reg_export%" (call :IncrementFileName "%last%")
    echo Windows Registry Editor Version 5.00>"!reg_export!"
    echo: >>"!reg_export!"
    echo !regfileline!>>"!reg_export!"
    goto :EOF
    
     
  5. GodHand

    GodHand MDL Addicted

    Jul 15, 2016
    534
    926
    30
    If I have the time today, and a full solution has not been posted, I will post how this can be done in PowerShell by just using regular expressions within a single loop process.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  6. KNARZ

    KNARZ MDL Addicted

    Oct 9, 2012
    895
    482
    30
    That would be great as this would be a nice challenge for me to learn from your code (have it side by side)
    I also would donate like 5-10$ via paypal to each of you or donate it to some NGO.
     
  7. KNARZ

    KNARZ MDL Addicted

    Oct 9, 2012
    895
    482
    30
    #7 KNARZ, Jan 19, 2020
    Last edited: Jan 19, 2020
    (OP)
    Pretty much done.

    Code:
    @echo off
    setlocal EnableDelayedExpansion
    set "filetoparse=%~dpnx1"
    
    REM shift the file into variables
    for /f "tokens=1* delims=:" %%a IN ('findstr.exe /n "^" "%filetoparse%"') do (
        set /a "Count+=1"
        set "Line_!count!=%%a"
        set "LineValue_!count!=%%b"
    )
    REM parse registry file adding line number
        for /f "tokens=1* delims=:" %%a IN ('findstr.exe /n "^" "%filetoparse%"') do (
        set "linenumber=%%a"
        set "regfileline=%%b"
        if "!regfileline:~0,3!"=="[HK"    (call :loopprocess)
        if "!regfileline:~0,4!"=="[-HK"    (call :delete)
    )
    echo Done
    exit /b
    
    :loopprocess
        set "regkeypath=!regfileline!"
        set "tok=0"
    REM count tokens
    :loopprocess2
    for /F "tokens=1* delims=[\]" %%G in ( "!regkeypath!" ) do (
        if !tok! EQU 0 (set reg_path=%%H)
        set /A "tok+=1"
        set regkeypath=%%H
        set lastregkeypath=%%G
        goto loopprocess2
    )
    call :CleanUpRegPath reg_path
    call :FolderCheckAndCreate reg_path
    call :CreateBaseFile
    call :RegValueLoop !linenumber!
    goto :EOF
    exit /b
    
    :RegValueLoop
    set /a "linenumber+=1"
    for /l %%i in (%~1 1 %Count%) do (
        REM set "CurrentLine="
        REM set "CurrentLineValue=
        if "!Line_%%i!" == "!linenumber!" (
        REM exit loop if next line is a registry key
        if "!LineValue_%%i:~0,3!"=="[HK"    (goto :EOF)
        if "!LineValue_%%i:~0,4!"=="[-HK"    (goto :EOF)
        REM print next line if line exist
        if defined LineValue_%%i (
            echo !LineValue_%%i!>>"%reg_export%"
        ) else (
            echo.>>"%reg_export%"
        )
        goto :RegValueLoop
        )
    )
    goto :EOF
    
    :CleanUpRegPath
    set "%~1=!%~1:[=!"
    set "%~1=!%~1:]=!"
    set "%~1=!%~1:\%lastregkeypath%=!"
    goto :EOF
    
    :FolderCheckAndCreate
    set "RegFullPath=%~dp0!%~1!"
    if not exist "%RegFullPath%" (mkdir "%RegFullPath%")
    goto :EOF
    
    :CreateBaseFile
    set "reg_export=%RegFullPath%\%lastregkeypath%.reg"
    REM if exist "%reg_export%" (call :IncrementFileName "%last%")
    echo Windows Registry Editor Version 5.00>"!reg_export!"
    echo. >>"!reg_export!"
    echo !LineValue_%linenumber%!>>"%reg_export%"
    goto :EOF
    
     
  8. Thomas Dubreuil

    Thomas Dubreuil MDL Senior Member

    Aug 29, 2017
    363
    620
    10
    #8 Thomas Dubreuil, Jan 19, 2020
    Last edited: Jan 20, 2020
    Increment file name "windows style" : name (1) name (2) etc...

    Code:
    :CreateBaseFile
        set "reg_export=%RegFullPath%\%lastregkeypath%.reg"
        if not exist "%reg_export%" ( call :Exportfile ) else ( call :IncrementFileName )
        goto :EOF
    
    :IncrementFileName
        setlocal EnableDelayedExpansion
        set "baseName=%lastregkeypath% ("
        set "n=0"
    :Incrementloop
        for /f "delims=" %%F in (
          '2^>nul dir /b /ad "%baseName%*)"^|findstr /xri /c:"%baseName%[0-9]*)"'
        ) do (
          set "name=%%F"
          set "name=!name:*%baseName%=!"
          set "name=!name:)=!"
          echo !name!
          if !name! gtr !n! set "n=!name!"
        )
        set /a n+=1
        set "newregkeypath=%baseName%%n%"
        set "reg_export=%RegFullPath%\%newregkeypath%).reg"
        if exist "%reg_export%" ( goto :Incrementloop )
    
    :Exportfile
        echo Windows Registry Editor Version 5.00>"!reg_export!"
        echo. >>"!reg_export!"
        echo !LineValue_%linenumber%!>>"%reg_export%"
        goto :EOF
    Full script with much less lines...
    Code:
    @echo off
    
    set "filetoparse=%~dpnx1"
    setlocal EnableDelayedExpansion
    for /f "tokens=1* delims=:" %%a IN ('findstr /n "^" "%filetoparse%"') do (
        set "linenumber=%%a"
        set "regkey=%%b"
        if "!regkey:~0,3!"=="[HK" ( call :loopprocess )
        if "!regkey:~0,4!"=="[-HK" ( call :delete )
    )
    <nul set /p DummyName=Done.
    timeout /t 2 /nobreak >nul 2>&1
    exit /b
    
    :loopprocess
    set "var1=!regkey!" &set "var2=!regkey!" &set "tok=0"
    :loopprocess2
    for /F "tokens=1* delims=[\]" %%G in ( "!var1!" ) do (
      set /A "tok+=1"
      set "var1=%%H"
      goto :loopprocess2
    )
    for /F "tokens=%tok% delims=[\]" %%J in ( "!var2!" ) do ( set "lastregkeypath=%%J" )
    set "RegFullPath=!regkey:HKEY_CURRENT_USER\=!" &set "RegFullPath=!RegFullPath:HKEY_LOCAL_MACHINE\=!"
    set "RegFullPath=!RegFullPath:HKCU\=!" &set "RegFullPath=!RegFullPath:HKLM\=!"
    set "RegFullPath=!RegFullPath:[=!" &set "RegFullPath=!RegFullPath:]=!"
    set "RegFullPath=!RegFullPath:%lastregkeypath%=!"
    set "RegFullPath=%~dp0!RegFullPath!"
    if not exist "%RegFullPath%" ( mkdir "%RegFullPath%" 2>nul )
    set "reg_export=%RegFullPath%\%lastregkeypath%.reg"
    if exist "!reg_export!" ( call :IncrementFileName )
    call :Exportfile
    :loopprocess3
    set /a "linenumber+=1"
    for /f "tokens=1* delims=:" %%S IN ('findstr /n "^" "%filetoparse%"') do (
        set "newlinenumber=%%S"
        set "linevalue=%%T"
        if "!newlinenumber!"=="!linenumber!" (
            if "!linevalue:~0,3!"=="[HK" ( goto :eof )
            if "!linevalue:~0,4!"=="[-HK" ( goto :eof )
            if defined linevalue (
                echo !linevalue!>>"!reg_export!"
                goto :loopprocess3
    )))
    goto :EOF
    
    :IncrementFileName
    set "baseName=%lastregkeypath% ("
    set "n=0"
    :Incrementloop
    for /f "delims=" %%F in (
      '2^>nul dir /b /ad "%baseName%*)"^|findstr /xri /c:"%baseName%[0-9]*)"'
    ) do (
      set "name=%%F"
      set "name=!name:*%baseName%=!"
      set "name=!name:)=!"
      echo !name!
      if !name! gtr !n! set "n=!name!"
    )
    set /a n+=1
    set "newregkeypath=%baseName%%n%"
    set "reg_export=%RegFullPath%\%newregkeypath%).reg"
    if exist "!reg_export!" ( goto :Incrementloop )
    goto :EOF
    
    :Exportfile
    echo Windows Registry Editor Version 5.00>"!reg_export!"
    echo: >>"!reg_export!"
    echo !regkey!>>"!reg_export!"
    goto :EOF
    Just saw now your comment about the findstr thingy...
    We can maybe use skip to skip "n" lines, but don't know if would be faster, you'll have to make a for /f loop anyway.

    ok last one :)
    Code:
    @echo off
    
    set "filetoparse=%~dpnx1"
    setlocal EnableDelayedExpansion
    for /f "tokens=1* delims=:" %%a IN ('findstr /n "^" "%filetoparse%"') do (
        set "linenumber=%%a"
        set "regfileline=%%b"
        if "!regfileline:~0,3!"=="[HK" ( call :loopprocess )
        if "!regfileline:~0,4!"=="[-HK" ( call :delete )
    )
    <nul set /p DummyName=Done.
    timeout /t 2 /nobreak >nul 2>&1
    exit /b
    
    :loopprocess
    set "var1=!regfileline!" &set "var2=!regfileline!" &set "tok=0"
    :loopprocess2
    for /F "tokens=1* delims=[\]" %%G in ( "!var1!" ) do (
      set /A "tok+=1"
      set "var1=%%H"
      goto :loopprocess2
    )
    for /F "tokens=%tok% delims=[\]" %%J in ( "!var2!" ) do ( set "lastregkeypath=%%J" )
    set "RegFullPath=!regfileline:HKEY_CURRENT_USER\=!" &set "RegFullPath=!RegFullPath:HKEY_LOCAL_MACHINE\=!"
    set "RegFullPath=!RegFullPath:HKCU\=!" &set "RegFullPath=!RegFullPath:HKLM\=!"
    set "RegFullPath=!RegFullPath:[=!" &set "RegFullPath=!RegFullPath:]=!"
    set "RegFullPath=!RegFullPath:%lastregkeypath%=!"
    set "RegFullPath=%~dp0!RegFullPath!"
    if not exist "%RegFullPath%" ( mkdir "%RegFullPath%" 2>nul )
    set "reg_export=%RegFullPath%\%lastregkeypath%.reg"
    if exist "!reg_export!" ( call :IncrementFileName )
    call :Exportfile
    :loopprocess3
    for /f "skip=%linenumber% usebackq delims=" %%S in ( "%filetoparse%" ) do (
        set "linevalue=%%S"
        if "!linevalue:~0,3!"=="[HK" ( goto :eof )
        if "!linevalue:~0,4!"=="[-HK" ( goto :eof )
        if defined linevalue (
            echo !linevalue!>>"!reg_export!"
            set /a "linenumber+=1"
            goto :loopprocess3
    ))
    goto :EOF
    
    :IncrementFileName
    set "baseName=%lastregkeypath% ("
    set "n=0"
    :Incrementloop
    for /f "delims=" %%F in (
      '2^>nul dir /b /ad "%baseName%*)"^|findstr /xri /c:"%baseName%[0-9]*)"'
    ) do (
      set "name=%%F"
      set "name=!name:*%baseName%=!"
      set "name=!name:)=!"
      echo !name!
      if !name! gtr !n! set "n=!name!"
    )
    set /a n+=1
    set "newregkeypath=%baseName%%n%"
    set "reg_export=%RegFullPath%\%newregkeypath%).reg"
    if exist "!reg_export!" ( goto :Incrementloop )
    goto :EOF
    
    :Exportfile
    echo Windows Registry Editor Version 5.00>"!reg_export!"
    echo: >>"!reg_export!"
    echo !regfileline!>>"!reg_export!"
    goto :EOF
    ps: I think you don't need to add a blank line, it should already add a CR at the end (unless you want the file to end with 2 blank lines)
    ps2: Later thought, to increment truely "Windows style" you can replace "n=0" with "n=1", as I think windows would not create "file (1)", but directly "file (2)".
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...