Script to replace line that contains text - powershell

I found another discussion on this, where there were numerous different options available but most of them didn't seem to work for myself or the original poster. I did however find an example that is confirmed working, however I am struggling with getting it to work and hoping for help.
I need to find a line that begins with "ServerName=" and replace this with my own line. I have used the example that was found and modified it, but I am getting errors when using it.
PowerShell Command:
powershell -Command "(Get-Content 'KFGame\Config\PCServer-KFGame.ini') | Foreach-Object {$_ -replace '^ServerName.$', ('ServerName=Network BUF 12345 Normal')} | Set-Content 'KFGame\Config\PCServer-KFGame.ini'"
Error Message:
) was unexpected at this time

If you want to run complex PowerShell statements from CMD you need to put them in quotes so that CMD just sees a string and doesn't try to handle special characters (like pipes):
powershell -Command "(Get-Content 'C:\Host400.txt') | Foreach-Object {$_ -replace '^workstationID.*$', (""WorkstationID=$computerName""} | Set-Content 'C:\Host400.txt'"
Note that you need to either escape or replace double quotes within the command string.
A better approach is to put the PowerShell statement(s) into a .ps1 script and run that via the -File parameter:
powershell -File "C:\path\to\your.ps1"

Related

Getting the error code from a command piped to `-replace` in powershell

I have a powershell script, where one of my commands' output is piped to -replace to make sure sensitive information isn't logged.
$(mycommand do stuff) -replace 'START_SECRET.*?END_SECRET' 'SECRET_ELIDED'
However, when mycommand fails, the error gets eaten by -replace and the script still succeeds where I would like it to fail. How can I get the error code from that command?
I'm open to solutions that don't look quite like this -replace, but ideally it would be something where mycommand output appears as it is generated, rather than all at once at the end like would happen if I saved the output, checked the error code, and grepped it later.
Your current example also finishes running before displaying any output. If you want to output as it comes you need to lose the $(...). Now each line will be sent down the pipe as they are generated
mycommand do stuff | ForEach-Object {$_ -replace 'START_SECRET.*?END_SECRET' 'SECRET_ELIDED' }
As for the error "being eaten", I'm not seeing this behavior, even when wrapping in $(...)
PS C:\temp> $(dir nonexistentpath) -replace '^', '----'
Get-ChildItem: Cannot find path 'C:\temp\nonexistentpath' because it does not exist.
even with native applications
PS C:\Program Files\GIMP 2\bin> $(.\bzip2.exe sklfjslf) -replace '^', '----'
bzip2.exe: Can't open input file sklfjslf: No such file or directory.
As others have mentioned it's not clear what your command is or even what you are doing with the output. If you are trying to capture/process the error records as well you will need to redirect them to the success stream using 2>&1
mycommand do stuff 2>&1 | ForEach-Object {$_ -replace 'START_SECRET.*?END_SECRET' 'SECRET_ELIDED' }
If the command you are running is a cmdlet you can also instead use -ErrorVariable to capture error records instead of redirecting them to the success stream.
PS C:\temp> dir nonexistentpath -ErrorVariable errors
Get-ChildItem: Cannot find path 'C:\temp\nonexistentpath' because it does not exist.
PS C:\temp> $errors
Get-ChildItem: Cannot find path 'C:\temp\nonexistentpath' because it does not exist.
$var = <#your stuff here#>
$var.Replace('START_SECRET.*?END_SECRET','SECRET_ELIDED')

Extra Space needs to remove while executing powershell

I am executing the below script on the windows server as a PowerShell script -
$today = (Get-Date).ToString('dd_MM_yyyy_HH_mm')
echo "Date & Time : $today"
powershell -Command "Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_"$today"'"
The above script is working fine but there is a blank space between ABC & Date while creating the directory. Please please help me on this how can I remove this blank space.
**Directory Name :** ModelFactoryProducti**on_ 28**_06_2021_11_05
**Directory Name Should be :** ModelFactoryProduction_28_06_2021_11_05
Since you're calling from PowerShell, the best option is to pass a script block to powershell.exe, the Windows PowerShell CLI.
However, this raises the question: why do you need to call the CLI from PowerShell to begin with, given that you could just call your Copy-Item command directly, without the overhead (and potential loss of type fidelity) that comes with creating another PowerShell session, via a child process.
if you still need to call the PowerShell CLI from PowerShell itself, use the following:
$today = (Get-Date).ToString('dd_MM_yyyy_HH_mm')
powershell -Command {
Copy-Item -Recurse 'C:\ABC' -Destination ('C:\ABC_' + $args[0])
} -args $today
As for what you tried:
Removing the " around $today in 'C:\ABC_"$today"' would have worked too - the outer "..." quoting would still have ensured that $today is expanded.
What you thought of as a single string argument,
"Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_"$today"'", was passed as two arguments:
Argument 1: Verbatim Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_, which, due to being a (double-)quoted token became its own argument - despite other characters immediately following it.
Argument 2: The value of $today, immediately followed by a verbatim ' (the value of "'"), e.g., verbatim 06_2021_11_05'
Not being able to compose a single argument from a mix of quoted and unquoted tokens if the first token happens to be quoted is a notable pitfall, discussed in detail in this answer.
When you use -Command and pass multiple arguments, PowerShell simply joins those arguments to form a single string by placing a space between them, before interpreting the resulting string as PowerShell code.
Therefore, your two verbatim arguments, Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_ and (e.g.) 06_2021_11_05' were ultimately interpreted as
Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_ 06_2021_11_05' - note the unwanted space.
See this answer for a comprehensive overview of the PowerShell CLI (covers both powershell.exe and pwsh).
Use the following:
$today = (Get-Date -format 'dd_MM_yyyy_HH_mm')
Copy-Item -Recurse 'C:\ABC' -Destination "C:\ABC_$today"
When possible, avoid using either of these kinds of syntax, as it's very simple to allow accidentally (or maliciously) inserting extra data into the command string. Plus, you're already in powershell - no need to execute powershell again unless you left it to run some cmd commands:
powershell -c "Write-Host $var"
Invoke-Expression -Command 'Write-Host "$var"'

Batch Replace Multiple Texts in a file

I need to replace multiple different instances of text in a file using a .bat.
(Note: SQF is just a server config file, Encoding is not an issue)
I have tried:
powershell -Command "(gc Structures.sqf) -replace 'WOOD_WALL', 'CINDERBLOCK_WALL' | Out-File BrickMe.sqf"
powershell -Command "(gc Structures.sqf) -replace 'e614ee17-4467-4d51-a3f2-d4faa61de89e', 'a59f5b71-2b9a-45a2-85bb-ec29a47f78aa' | Out-File BrickMe.sqf"
powershell -Command "(gc Structures.sqf) -replace 'Wood Wall', 'Cinderblock Wall' | Out-File BrickMe.sqf"
But it only performs the last command. Eg. WOOD_WALL remains.
And
sed -e "s/WOOD_WALL/CINDERBLOCK_WALL/" -e "s/Wood Wall/Cinderblock Wall/" <Structures.sqf>BrickMe.sqf
Just creates an empty file called BrickMe.sqf
I have also tried a VBScript to no avail. I would rather keep it able to run on any Windows machine provided the code can handle the file size but I don't know how to replace multiple instances of different text without repeating the whole command and taking a long time.
I have also looked at this http://www.dostips.com/?t=batch.findandreplace
But was unsure of where to put my "WOOD_WALL" instances and my file names.
I have found heaps of results on google for replacing 1 piece of text in multiple files but hardly anything on replacing multiple texts in 1 file.
The Story:
I am running an Arma 3 server and have built a wooden admin base in game that I wish to convert to a cinderblock base. I have done this before but only manually by replacing the Instance Name, Instance_ID and Entity Name. I would like to do it using a batch file if possible and upload it to http://www.exilemod.com to help other admins. The files are usually no larger then 15 Megabytes in size.
I'm pretty awesome with Windows batch files but am new to PowerShell.
It's no wonder that your actions are not successfull.
You always take the original input file, perform actions and save to the same output file this way overwriting the previous action.
For better understanding the PowerShell part broken up:
(gc Structures.sqf) -replace 'WOOD_WALL', 'CINDERBLOCK_WALL' `
-replace 'e614ee17-4467-4d51-a3f2-d4faa61de89e',
'a59f5b71-2b9a-45a2-85bb-ec29a47f78aa' `
-replace 'Wood Wall', 'Cinderblock Wall' |
Out-File BrickMe.sqf
As the -replace operator is RegEx based the 1st and 3rd replace can be joined by:
placing a character class [_ ] underscore or space inside
a capture group ([_ ]) and
using the capture group in the replacement string $1
(gc Structures.sqf) -replace 'WOOD([_ ])WALL', 'CINDERBLOCK$1WALL' `
-replace 'e614ee17-4467-4d51-a3f2-d4faa61de89e',
'a59f5b71-2b9a-45a2-85bb-ec29a47f78aa' |
Out-File BrickMe.sqf
All this wrapped again in a cmd line:
powershell -NoProfile -Command "(gc Structures.sqf) -replace 'WOOD([_ ])WALL', 'CINDERBLOCK$1WALL' -replace 'e614ee17-4467-4d51-a3f2-d4faa61de89e','a59f5b71-2b9a-45a2-85bb-ec29a47f78aa'|Out-File BrickMe.sqf"

How do I concatenate two text files in PowerShell?

I am trying to replicate the functionality of the cat command in Unix.
I would like to avoid solutions where I explicitly read both files into variables, concatenate the variables together, and then write out the concatenated variable.
Simply use the Get-Content and Set-Content cmdlets:
Get-Content inputFile1.txt, inputFile2.txt | Set-Content joinedFile.txt
You can concatenate more than two files with this style, too.
If the source files are named similarly, you can use wildcards:
Get-Content inputFile*.txt | Set-Content joinedFile.txt
Note 1: PowerShell 5 and older versions allowed this to be done more concisely using the aliases cat and sc for Get-Content and Set-Content respectively. However, these aliases are problematic because cat is a system command in *nix systems, and sc is a system command in Windows systems - therefore using them is not recommended, and in fact sc is no longer even defined as of PowerShell Core (v7). The PowerShell team recommends against using aliases in general.
Note 2: Be careful with wildcards - if you try to output to inputFiles.txt (or similar that matches the pattern), PowerShell will get into an infinite loop! (I just tested this.)
Note 3: Outputting to a file with > does not preserve character encoding! This is why using Set-Content is recommended.
Do not use >; it messes up the character encoding. Use:
Get-Content files.* | Set-Content newfile.file
In cmd, you can do this:
copy one.txt+two.txt+three.txt four.txt
In PowerShell this would be:
cmd /c copy one.txt+two.txt+three.txt four.txt
While the PowerShell way would be to use gc, the above will be pretty fast, especially for large files. And it can be used on on non-ASCII files too using the /B switch.
You could use the Add-Content cmdlet. Maybe it is a little faster than the other solutions, because I don't retrieve the content of the first file.
gc .\file2.txt| Add-Content -Path .\file1.txt
To concat files in command prompt it would be
type file1.txt file2.txt file3.txt > files.txt
PowerShell converts the type command to Get-Content, which means you will get an error when using the type command in PowerShell because the Get-Content command requires a comma separating the files. The same command in PowerShell would be
Get-Content file1.txt,file2.txt,file3.txt | Set-Content files.txt
I used:
Get-Content c:\FileToAppend_*.log | Out-File -FilePath C:\DestinationFile.log
-Encoding ASCII -Append
This appended fine. I added the ASCII encoding to remove the nul characters Notepad++ was showing without the explicit encoding.
If you need to order the files by specific parameter (e.g. date time):
gci *.log | sort LastWriteTime | % {$(Get-Content $_)} | Set-Content result.log
You can do something like:
get-content input_file1 > output_file
get-content input_file2 >> output_file
Where > is an alias for "out-file", and >> is an alias for "out-file -append".
Since most of the other replies often get the formatting wrong (due to the piping), the safest thing to do is as follows:
add-content $YourMasterFile -value (get-content $SomeAdditionalFile)
I know you wanted to avoid reading the content of $SomeAdditionalFile into a variable, but in order to save for example your newline formatting i do not think there is proper way to do it without.
A workaround would be to loop through your $SomeAdditionalFile line by line and piping that into your $YourMasterFile. However this is overly resource intensive.
To keep encoding and line endings:
Get-Content files.* -Raw | Set-Content newfile.file -NoNewline
Note: AFAIR, whose parameters aren't supported by old Powershells (<3? <4?)
I think the "powershell way" could be :
set-content destination.log -value (get-content c:\FileToAppend_*.log )

How to enter a multi-line command

Is it possible to split a PowerShell command line over multiple lines?
In Visual Basic I can use the underscore (_) to continue the command in the next line.
You can use a space followed by the grave accent (backtick):
Get-ChildItem -Recurse `
-Filter *.jpg `
| Select LastWriteTime
However, this is only ever necessary in such cases as shown above. Usually you get automatic line continuation when a command cannot syntactically be complete at that point. This includes starting a new pipeline element:
Get-ChildItem |
Select Name,Length
will work without problems since after the | the command cannot be complete since it's missing another pipeline element. Also opening curly braces or any other kind of parentheses will allow line continuation directly:
$x=1..5
$x[
0,3
] | % {
"Number: $_"
}
Similar to the | a comma will also work in some contexts:
1,
2
Keep in mind, though, similar to JavaScript's Automatic Semicolon Insertion, there are some things that are similarly broken because the line break occurs at a point where it is preceded by a valid statement:
return
5
will not work.
Finally, strings (in all varieties) may also extend beyond a single line:
'Foo
bar'
They include the line breaks within the string, then.
I just found out that there must not be any character between the back tick and the line break. Even whitespace will cause the command to not work.
In most C-like languages I am deliberate about placing my braces where I think they make the code easiest to read.
PowerShell's parser recognizes when a statement clearly isn't complete, and looks to the next line. For example, imagine a cmdlet that takes an optional script block parameter:
Get-Foo { ............ }
if the script block is very long, you might want to write:
Get-Foo
{
...............
...............
...............
}
But this won't work: the parser will see two statements. The first is Get-Foo and the second is a script block. Instead, I write:
Get-Foo {
...............
...............
...............
}
I could use the line-continuation character (`) but that makes for hard-to-read code, and invites bugs.
Because this case requires the open brace to be on the previous line, I follow that pattern everywhere:
if (condition) {
.....
}
Note that if statements require a script block in the language grammar, so the parser will look on the next line for the script block, but for consistency, I keep the open brace on the same line.
Simlarly, in the case of long pipelines, I break after the pipe character (|):
$project.Items |
? { $_.Key -eq "ProjectFile" } |
% { $_.Value } |
% { $_.EvaluatedInclude } |
% {
.........
}
To expand on cristobalito's answer:
I assume you're talking about on the command-line - if it's in a script, then a new-line >acts as a command delimiter.
On the command line, use a semi-colon ';'
For example:
Sign a PowerShell script on the command-line. No line breaks.
powershell -Command "&{$cert=Get-ChildItem –Path cert:\CurrentUser\my -codeSigningCert ; Set-AuthenticodeSignature -filepath Z:\test.ps1 -Cert $cert}
In PowerShell and PowerShell ISE, it is also possible to use Shift + Enter at the end of each line for multiline editing (instead of standard backtick `).
Just add a corner case here. It might save you 5 minutes. If you use a chain of actions, you need to put "." at the end of line, leave a space followed by the "`" (backtick). I found this out the hard way.
$yourString = "HELLO world! POWERSHELL!". `
Replace("HELLO", "Hello"). `
Replace("POWERSHELL", "Powershell")
Just use ` character to separate command on multiline
If you are trying to separate strings into multiple lines, you can use the "+". For example:
$header = "Make," +
"ComputerName," +
"Model," +
"Windows Version"
Will look just like:
$header = "Make,ComputerName,Model,Windows Version"
I started by doing
if ($true) {
"you can write multiple lines here, and the command doesn't run untill you close the bracket"
"like this"
}
Recently found out I could just
&{
get-date
"more stuff"
}
I assume you're talking about on the command-line - if it's in a script, then a new-line acts as a command delimiter.
On the command line, use a semi-colon ';'
Use a semi-colon ; to separate command
Replace double backslash \\ on any backslashes \.
Use "' for passing safe address to switch command like "'PATH'".
This ps1 command install locale pfx certificate.
powershell -Command "$pfxPassword = ConvertTo-SecureString -String "12345678" -Force -AsPlainText ; Import-PfxCertificate -FilePath "'C:\\Program Files\\VpnManagement\\resources\\assets\\cert\\localhost.pfx'" Cert:\\LocalMachine\\My -Password $pfxPassword ; Import-PfxCertificate -FilePath "'C:\\Program Files\\VpnManagement\\resources\\assets\\cert\\localhost.pfx'" Cert:\\LocalMachine\\Root -Password $pfxPassword"
This is an old post, so here's the modern method.
If you're not using legacy powershell, the cleanest way to continue lines is the pipe at the start of the line.
Note: The command doesn't break with some lines commented out. This is great on the command line.
> Get-ChildItem -path 'c:\' -Depth 1
| Sort-Object LastWriteTime
# | Sort-Object Length -Descending
| Select-Object -First 3 -Skip 3
| Foreach-Object {
$_.Name, $_.Length | Join-String -Separator ' = '
}
output:
explorer.exe = 4826160
procexp.old.exe = 2925760
RtlExUpd.dll = 2839488
Windows Powershell ( Version < 6 )
Unfortunately windows powershell does not support it. A bunch of alternatives are linked above. You can remove the backtick completely: 2017/07/bye-bye-backtick-natural-line
There's sooo many ways to continue a line in powershell, with pipes, brackets, parentheses, operators, dots, even with a comma. Here's a blog about it: https://get-powershellblog.blogspot.com/2017/07/bye-bye-backtick-natural-line.html
You can continue right after statements like foreach and if as well.
$scriptBlock = [Scriptblock]::Create(#'
echo 'before'
ipconfig /all
echo 'after'
'#)
Invoke-Command -ComputerName AD01 -ScriptBlock $scriptBlock
source
don't use backquote
In windows terminal (powershell profile) I can simply click Shift-Enter works fine for me.
PS C:\xxx2021> Get-ChildItem -Include *remote* -Recurse |
>> Sort-Object -Property LastWriteTime -Descending |
>> Select-Object LastWriteTime, Name -First 25
LastWriteTime Name
------------- ----
12/5/2021 5:04:02 PM remote-control-car-BatteryPack-Number-2021-12-03.pdf
PS C:\xxx2021>enter code here