Search code examples
powershellbatch-filefind-replace

Replace multiple strings in txt file using Batch and Powershell


I have built a batch file that runs a BTEQ script and returns the data in a txt file. However, I am expanding my code to allow for user input of user_id, password, and a period parameter YYYYMM. I initially wrote hard coded my personal login information into the BTEQ file and tested with just the snapshot parameter like so.

echo off
SETLOCAL ENABLEEXTENSIONS DISABLEDELAYEDEXPANSION
SET /P SNAPSHOT="What snapshot should I use [YYYYMM]?"
DEL EXPORT.TXT
POWERSHELL -COMMAND "(GET-CONTENT TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE \"\?YYYYMM\"", %SNAPSHOT% "} | OUT-FILE TEST.TXT";
bteq < test.txt > output.txt 2>&1
POWERSHELL -COMMAND "(GET-CONTENT TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE " %SNAPSHOT% ,"\"?YYYYMM\"} | OUT-FILE TEST.TXT";
@echo off goto end
en @echo exit

The code worked as intended. However, when I go to add the userid and password, I simply copy the snapshot code for PowerShell and replaced the variables as needed.

echo off
SETLOCAL ENABLEEXTENSIONS DISABLEDELAYEDEXPANSION
SET /P USER_ID=Enter User ID:
SET /P USER_PASSWORD=Enter Password:
SET /P SNAPSHOT="What snapshot should I use [YYYYMM]?"
DEL EXPORT.TXT
POWERSHELL -COMMAND "(GET-CONTENT TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE \"$USER_ID\"", %USER_ID% "} | OUT-FILE TEST.TXT";
POWERSHELL -COMMAND "(GET-CONTENT TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE \"\$USERPASSWORD\"", %USER_PASSWORD% "} | OUT-FILE TEST.TXT"; 
POWERSHELL -COMMAND "(GET-CONTENT TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE \"\?YYYYMM\"", %SNAPSHOT% "} | OUT-FILE TEST.TXT";
bteq < test.txt > output.txt 2>&1
::POWERSHELL -COMMAND "(GET-CONTENT TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE " %SNAPSHOT% ,"\"?YYYYMM\"} | OUT-FILE TEST.TXT";
@echo off goto end
en @echo exit

The snapshot code still works but I get errors for the User_ID and User_Password user inputs. As follows:

Enter User ID:userTest
Enter Password:TestPass
What snapshot should I use [YYYYMM]?202102

At line:1 char:66
+ ... -CONTENT TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE "\$USER_ID", userTes ...
+                                                                  ~
Missing expression after ','.
At line:1 char:67
+ ... TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE "\$USER_ID", userTest } | OUT ...
+                                                          ~~~~~~~~
Unexpected token 'userTest' in expression or statement.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingExpressionAfterToken

At line:1 char:71
+ ... ENT TEST.TXT) | FOREACH-OBJECT {$_ -REPLACE "\$USERPASSWORD", TestPas ...
+                                                                  ~
Missing expression after ','.
At line:1 char:72
+ ... TXT) | FOREACH-OBJECT {$_ -REPLACE "\$USERPASSWORD", TestPass } | OUT ...
+                                                          ~~~~~~~~
Unexpected token 'TestPass' in expression or statement.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingExpressionAfterToken

Solution

  • Here is my suggestion, using the syntax as suggested in my comments, but instead of multiple instances of PowerShell, a single one:

    %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "(Get-Content -Path \".\test.txt\") | ForEach-Object { $_.Replace(\"`$USER_ID\", \"%USER_ID%\").Replace(\"`$USERPASSWORD\", \"%USER_PASSWORD%\").Replace(\"?YYYYMM\", \"%SNAPSHOT%\") } | Set-Content -Path \".\test.txt\""
    

    If you prefer to use single quotes in PowerShell, (which in this case would be simpler to read and understand), then:

    %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "(Get-Content -Path '.\test.txt') | ForEach-Object { $_.Replace('$USER_ID', '%USER_ID%').Replace('$USERPASSWORD', '%USER_PASSWORD%').Replace('?YYYYMM', '%SNAPSHOT%') } | Set-Content -Path '.\test.txt'"
    

    These examples are based upon my interpretation of what your code is supposed to replace in test.txt, i.e. the literal strings, $USER_ID, $USERPASSWORD and ?YYYYMM.

    To return the string value of %SNAPSHOT% back to the literal string ?YYYYMM, after having ran your bteq command, (as you have in your code), then you could use either:

    %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "(Get-Content -Path \".\test.txt\") | ForEach-Object { $_.Replace(\"%SNAPSHOT%\", \"?YYYYMM\") } | Set-Content -Path \".\test.txt\""
    

    Or

    %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "(Get-Content -Path '.\test.txt') | ForEach-Object { $_.Replace('%SNAPSHOT%', '?YYYYMM') } | Set-Content -Path '.\test.txt'"
    

    If you are returning all three strings, not just one, then you could just use your chosen the first example, and switch the places of each string pair. However, for that, I seems simpler to me, to just Set-Conent to a new file, use that, then delete, than to keep modifying the same file.