windowspowershellbatch-filehybrid

PowerShell in Batch hybrid - How to find several different strings in a txt and append to the top only if it does not exist?


This is my attempt at learning PowerShell. No edu background in coding/scripting. Only learned Batch by reading different examples here in StackOverflow and Superuser, trying to adapt found snippets to what is needed, and a lot of trial and error. Please be kind!

There are several strings (without the numbering) that needs to be added to a txt file sequentially and to the top of the file:

  1. [/This/IsJust.AnExample]
  2. IsThisTrue=False
  3. IsThisFalse=

I attempted to "convert" what was found to something that might look usable in a Batchfile:

start "" /wait /min powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "(Get-ChildItem 'D:\Sample Path\%sample_var%\Sample file.txt' -File -recurse | Where{!(Select-String -SimpleMatch '([/This/IsJust.AnExample])|(IsThisTrue=False)|(IsThisFalse=)' -Path $_.fullname -Quiet)} | ForEach{ $Path = $_.FullName '([/This/IsJust.AnExample])|(IsThisTrue=False)|(IsThisFalse=)',(Get-Content $Path) | Set-Content $Path })"

Expectation:

  1. Look for each string. If it doesn't exist, add it

Example: If it's entirely missing

[/MoreRandom /Example/In /Here]
MaybeThisIsCorrect=False
MaybeIAmConfused=True
MaybeINeedToLearnTheBasicsOfPowershell=True
MyEnglishIsTerrible=True
Apologetic=True

Expected:

[/This/IsJust.AnExample]
IsThisTrue=False
IsThisFalse=

[/MoreRandom /Example/In /Here]
MaybeThisIsCorrect=False
MaybeIAmConfused=True
MaybeINeedToLearnTheBasicsOfPowershell=True
MyEnglishIsTerrible=True
Apologetic=True

Example:

<newline/blank>
<last string>
<newline/blank>
<string1>
<newline/blank>
<existing content>

Expected:

<string1>
<string2>
<last string>
<newline/blank>
<existing content>

Example:

<newline/blank>
<newline/blank>
<newline/blank>
<string1>
<last string>
<string2>
<newline/blank>
<existing content>

Expected:

<string1>
<string2>
<last string>
<newline/blank>
<existing content>
<last string>
<newline/blank>
<existing content starts here>
  1. All must be inline, no external files such as .ps1, .bat, .txt, etc.
  2. It doesn't need an -include since it's only searching in a specific file.
  3. Must be usable in a batchfile because it's gonna be added to a for loop.
  4. Always able to handle spaces in file paths
  5. Must be able to handle encoding with BOM (some snippets added random non-Latin characters)

Result of original attempt:

Result of attempts after receiving suggestions/info:

Notes:

What is causing it to fail and how to properly do it?

A link to a dummies guide on how to convert PowerShell to Batch would also be nice.

Additionally, would appreciate to know which part can be reused in order to add more strings if needed in the future.


Solution

  • Mmm... If you want that the solution works in a Batch file, why not write it in a Batch file in first place? Besides, IMHO the Batch solution is simpler than the PowerShell one and it runs much faster than the PS one, specially if you want to run it in a loop...

    EDIT 2023/10/16: Code modified as requested in comments...

    @echo off
    setlocal EnableDelayedExpansion
    
    rem Define the strings that needs to be reviewed
    set "n=0"
    set "strings="
    for %%a in (
       "[/This/IsJust.AnExample]"
       "IsThisTrue=False"
       "IsThisFalse="
       ) do (
       set /A n+=1
       set "string[!n!]=%%~a"
       set "strings=!strings!/C:"%%~a" "
    )
    
    rem Get the line number of the first line of "existing content"
    set "skip="
    for /F "tokens=1* delims=:" %%a in ('findstr /N /V /L %strings% test.txt') do (
       if not defined skip if "%%b" neq "" set /A "skip=%%a-1"
    )
    if defined skip (
       if "%skip%" neq "0" (
          set "skip=skip=%skip%"
       ) else (
          set "skip="
       )
    )
    
    rem Insert the strings at beginning of output file
    (
    for /L %%i in (1,1,%n%) do echo !string[%%i]!
    echo/
    for /F "%skip% delims=" %%a in (test.txt) do echo %%a
    ) > output.txt
    
    rem Last step: update input file
    REM move /Y output.txt test.txt
    

    If you want to run this code in a loop you could directly insert it in the loop, or you can convert it into a subroutine (adding a :label at beginning and an exit /B at end) and call :label in the loop

    PS - You said you need this code run in a for loop, but you don't specify which value will be modified in the loop... If you do so, we could write the complete solution

    EDIT 2023/10/17 New code added because there are A LOT of changes!

    This question have been modified several times with new specifications each time it is modified. Let's review your examples and descriptions so far:

    You have explained A LOT of things, but not the important ones...

    You should help us to help you, not hide important information that makes it difficult to help you... :(

    I hope the new code below solve you problem (at least, the last one)...

    @echo off
    setlocal EnableDelayedExpansion
    
    rem Define the strings that needs to be reviewed
    set "n=0"
    set "strings="
    for %%a in (
       "[/This/IsJust.AnExample]"
       "IsThisTrue=False"
       "IsThisFalse="
       ) do (
       set /A n+=1
       set "string[!n!]=%%~a"
       set "strings=!strings!/C:"%%~a" "
    )
    
    rem Generate the output file:
    (
    rem First, insert the strings at beginning
    for /L %%i in (1,1,%n%) do echo !string[%%i]!
    echo/
    rem Then, insert the lines in the file that have NOT the strings
    for /F "delims=" %%a in ('findstr /V /L %strings% test.txt') do echo %%a
    ) > output.txt
    
    rem Last step: update input file
    REM move /Y output.txt test.txt
    

    LAST CODE: I give up...

    @echo off
    setlocal EnableDelayedExpansion
    
    rem Define the strings that needs to be reviewed
    set "n=0"
    set "strings="
    for %%a in (
       "[/This/IsJust.AnExample]"
       "IsThisTrue=False"
       "IsThisFalse="
       ) do (
       set /A n+=1
       set "string[!n!]=%%~a"
       set "strings=!strings!/C:"%%~a" "
    )
    
    rem Process files with .ini extension
    for %%f in (*.ini) do (
    
       rem Generate the output file:
       (
       rem First, insert the strings at beginning
       for /L %%i in (1,1,%n%) do echo !string[%%i]!
       echo/
       rem Then, insert the lines in the file that have NOT the strings
       rem Convert a UTF-16 LE BOM file to UTF-8 via TYPE command
       for /F "delims=" %%a in ('type "%%f" ^| findstr /V /L %strings%') do echo %%a
       ) > output.txt
    
       rem Last step: update input file
       move /Y output.txt "%%f" > NUL
    
    )