How I can replace string which contains quotes and brackets in a file using powershell ?
I have file with settings c:\app\settings.js
app_set("safe.mode", true);
app_set("api.uri", "https://apiurl.com");
app_set("api.version", 1);
And script ChangeSettins.ps1 but it actually doesn't change strings in a file.
$SettingsFile = c:\app\settings.js
$SAFEMODE = 'app_set("safe.mode", true);'
$APIURI = 'app_set("api.uri", "https://apiurl.com");'
$APIVERSION = 'app_set("api.version", 1);'
$SAFEMODE_PROFILE = Get-Content $SettingsFile | Select-String -Pattern "safe.mode"
#$SAFEMODE_PROFILE
$APIURI_PROFILE = Get-Content $SettingsFile | Select-String -Pattern "api.uri"
#$APIURI_PROFILE
$APIVERSION_PROFILE = Get-Content $SettingsFile | Select-String -Pattern "api.version"
#$APIVERSION_PROFILE
If ("$SAFEMODE_PROFILE" -eq "$SAFEMODE") {
Write-Host "safe mode is enabled"
}
Else {
Write-Host "enabling safe mode"
(Get-Content $SettingsFile) | Foreach-Object { $_ -Replace "$SAFEMODE_PROFILE", "$SAFEMODE" } | Set-Content $SettingsFile
}
If ("$APIURI_PROFILE" -eq "$APIURI") {
Write-Host "uri is correct"
}
Else {
Write-Host "updating uri"
(Get-Content $SettingsFile) | Foreach-Object { $_ -Replace "$APIURI_PROFILE", "$APIURI" } | Set-Content $SettingsFile
}
If ("$APIVERSION_PROFILE" -eq "$APIVERSION") {
Write-Host "api version is 1"
}
Else {
Write-Host "changing api version to 1"
(Get-Content $SettingsFile) | Foreach-Object { $_ -Replace "$APIVERSION_PROFILE", "$APIVERSION" } | Set-Content $SettingsFile
}
Replacement Operator
The -replace operator replaces all or part of a value with the
specified value using regular expressions.
Apply the Regex.Escape(String) Method where necessary, e.g. as follows:
$_ -Replace [regex]::Escape("$APIURI_PROFILE"), "$APIURI"
Related
I have a script that seems to work correctly only it works to good.
I have files that contain multiple lines with the string "PROCEDURE DIVISION.", with the period at the end.
What I need to do...
ONLY remove the [2nd occurrence] of the string "PROCEDURE DIVISION." if it's in the text file twice and bypass the file if it is only found once. I need to preserve the 1st occurrence and change/remove the 2nd occurrence.
I can find and replace all the occurrences easily, I have no clue how to replace only 1 of 2.
Is this possible using Powershell?
Here is my code so far...
Get-ChildItem 'C:\Temp\*.cbl' -Recurse | ForEach {#
(Get-Content $_ | ForEach { $_ -replace "PROCEDURE DIVISION\.", " "}) | Set-Content $_
}
UPDATE
I got this to work and it's not pretty.
The only problem is is is capturing the string in the comments section.
What I need to do is only count the string as a hit when it's found starting in position 8 on each line.
Is that possible?
Get-ChildItem 'C:\Thrivent\COBOL_For_EvolveWare\COBOL\COBOL\*.*' -Recurse | ForEach {
($cnt=(Get-Content $_ | select-string -pattern "PROCEDURE DIVISION").length)
if ($cnt -gt "1") {
(Get-Content $_ | ForEach { $_ -replace "PROCEDURE DIVISION\.", " "}) | Set-Content $_
$FileName = $_.FullName
Write-Host "$FileName = $cnt" -foregroundcolor green
}
There are potential issues with all of the provided answers. Reading a file using switch statement is likely going to be the fastest method. But it needs to take into account PROCEDURE DIVISION. appearing multiple times on the same line. The method below will be more memory intensive than using switch but will consider the multi-match, single line condition. Note that you can use -cmatch for case- sensitive matching.
# Matches second occurrence of match when starting in position 7 on a line
Get-ChildItem 'C:\Temp\*.cbl' -Recurse -File | ForEach-Object {
$text = Get-Content -LiteralPath $_.Fullname -Raw
if ($text -match '(?sm)(\A.*?^.{6}PROCEDURE DIVISION\..*?^.{6})PROCEDURE DIVISION\.(.*)\Z') {
Write-Host "Changing file $($_.FullName)"
$matches.1+$matches.2 | Set-Content $_.FullName
}
}
This maybe a bit of a hack, but it works. $myMatches = $pattern.Matches in the case below gives us 3 matches, $myMatches[1].Index is the position of the second occurrence of the string you want to replace.
$text = "Hello foo, where are you foo? I'm here foo."
[regex]$pattern = "foo"
$myMatches = $pattern.Matches($text)
if ($myMatches.count -gt 1)
{
$newtext = $text.Substring(0,$myMatches[1].Index) + "bar" + $text.Substring($myMatches[1].Index + "foo".Length)
$newtext
}
try this:
$Founded=Get-ChildItem 'C:\Temp\' -Recurse -file -Filter "*.cbl" | Select-String -Pattern 'PROCEDURE DIVISION.' -SimpleMatch | where LineNumber -GT 1 | select Path -Unique
$Founded | %{
$Nb=0
$FilePath=$_.Path
$Content=Get-Content $FilePath | %{
if($_ -like '*PROCEDURE DIVISION.*')
{
$Nb++
if ($Nb -gt 1)
{
$_.replace('PROCEDURE DIVISION.', '')
}
else
{
$_
}
}
else
{
$_
}
}
$Content | Set-Content -Path $FilePath
}
You could use switch for this:
Get-ChildItem -Path 'C:\Temp' -Filter '*.cbl' -File -Recurse | ForEach-Object {
$occurrence = 0
$contentChanged = $false
$newContent = switch -Regex -File $_.FullName {
'PROCEDURE DIVISION\.' {
$occurrence++
if ($occurrence -eq 2) {
$_ -replace 'PROCEDURE DIVISION\.', " "
$contentChanged = $true
}
else { $_ }
}
default { $_ }
}
# only rewrite the file if a change has been made
if ($contentChanged) {
Write-Host "Updating file '$($_.FullName)'"
$newContent | Set-Content -Path $_.FullName -Force
}
}
Hi i have powershell where i am using a foreach-object and would like to skip the first iteration always. And i am using continue statement as well. But the current behaviour of continue is like break. Please suggest if something i am doing wrong here.
Below is the sample code.
$xmlfile = 'D:\testdirecotry\sample.xml'
[xml]$xmlcontent = (Get-Content $xmlfile)
$folderprefix = 'plm_z'
$regex = '<!--__AMAZONSITE id="(.+?)" instance="(.+?)"__-->'
$i=0
(Get-Content $xmlfile) | select-string -Pattern $regex | ForEach-Object {
write-host "Test Iterartion"
if($i -eq 0)
{
write-host "entering if loop"
write-host $i
$i++
write-host $i
continue
}
else
{
write-host "entering else loop"
write-host $_
$pscustomobject=#(
# write-host $_
$id = $_.Matches.Groups[1].Value
$instance = $_.Matches.Groups[2].Value
write-host "Do Something"
)
}
}
An easier way to skip the first object would potentially be to use Select-Object in the pipeline:
Get-Content $xmlfile |
Select-string -Pattern $regex |
Select-Object -Skip 1 |
ForEach-Object {
...
I have the following foreach loop in a powershell script:
foreach ($sqlScript in Get-ChildItem -path "$pathToScripts" -Filter *.sql | sort-object) {
Write-Host "Running Script " $sqlScript.Name
#Execute the query
switch ($removeComments) {
$true {
(Get-Content $sqlScript.FullName -Encoding UTF8 | Out-String) -replace '(?s)/\*.*?\*/', " " -split '\r?\n\s*go\s*\r\n?' -notmatch '^\s*$' |
ForEach-Object { $SqlCmd.CommandText = $_.Trim(); $reader = $SqlCmd.ExecuteNonQuery() }
}
$false {
(Get-Content $sqlScript.FullName -Encoding UTF8 | Out-String) -split '\r?\n\s*go\s*\r\n?' -notmatch '^\s*$' |
ForEach-Object { $SqlCmd.CommandText = $_.Trim(); $reader = $SqlCmd.ExecuteNonQuery() }
}
}
}
The entire script is wrapped with a catch/try block and works OK for errors.
I now have a requirement that if one of the files(scripts) produces an error, the loop will ignore that and move on to the next file.
Could I do this with nested catch try blocks or is there a way to resume the loop on an error?
Adding an additional Try..Catch would probably be the way to go. For example you could modify your ForEach-Object blocks as follows:
Try {
$SqlCmd.CommandText = $_.Trim();
$reader = $SqlCmd.ExecuteNonQuery()
} Catch {
Write-Error "$($SqlCmd.CommandText) resulted in an error"
$_
}
Note the $_ within the Catch block would contain the error that occurred. After the Catch is executed the rest of the script should then carry on as normal.
Use the ErrorAction preference as SilentlyContinue wherever there is an executing statement or a statement which you feel is likely to throw an error. In you current script, you can do this -
foreach ($sqlScript in Get-ChildItem -path "$pathToScripts" -Filter *.sql | sort-object)
{
Write-Host "Running Script " $sqlScript.Name
#Execute the query
switch ($removeComments)
{
$true
{
(Get-Content $sqlScript.FullName -Encoding UTF8 | Out-String) -replace '(?s)/\*.*?\*/', " " -split '\r?\n\s*go\s*\r\n?' -notmatch '^\s*$' -ErrorAction SilentlyContinue |
ForEach-Object { $SqlCmd.CommandText = $_.Trim(); $reader = $SqlCmd.ExecuteNonQuery() }
}
$false
{
(Get-Content $sqlScript.FullName -Encoding UTF8 | Out-String) -split '\r?\n\s*go\s*\r\n?' -notmatch '^\s*$' -ErrorAction SilentlyContinue |
ForEach-Object { $SqlCmd.CommandText = $_.Trim(); $reader = $SqlCmd.ExecuteNonQuery() }
}
}
}
See this and this link for details.
I am trying to create a log reader. The data looks like so:
2017-11-27 13:24:41,791 [8] INFO CTSipEndpoint.CLogger.provider.gsiplib [(null)] - -00001 [Info] Info | 4744 | REGISTERdialog[1] 2-e:5;t:1-3 (dn:85188)
2017-11-27 13:24:41,791 [8] INFO CTSipEndpoint.CLogger.provider.gsiplib [(null)] - -00001 [Info] Info | 4744 | REGISTERdialog[1] event 2 REG/accepted
I am trying to do the following:
Return only lines in the last 48 hours to query further.
From above return any lines that contain the following phrases: "error"
"device","does not exist", "Could not identify speaker!","warn"
So far i have only been able to get this to work in an inefficient way, which runs against the file for each phrase and appends an array. Unfortunately this means that the date time becomes non-sequential. I need to now sort the content object at the end of the script to it be in sequence, or find a way to run this query smarter. Here is my script for reference:
$logfile = "C:\users\test\desktop\programlogs.log"
$content = ""
cat $logfile |
Select-String "ERROR" -SimpleMatch |
select -expand line |
foreach {
$_ -match '(.+)\s\[(ERROR)\]\s(.+)'| Out-Null
$error_time = [datetime]($matches[1]).split(",")[0]
if ($error_time -gt (Get-Date).AddDays(-2)) {
$content += $_ + "`n"
}
}
cat $logfile |
Select-String "device" -SimpleMatch |
select -expand line |
foreach {
$_ -match '(.+)\s\[(device)\]\s(.+)'| Out-Null
$error_time = [datetime]($matches[1]).split(",")[0]
if ($error_time -gt (Get-Date).AddDays(-2)) {
$content += $_ + "`n"
}
}
cat $logfile |
Select-String "does not exist" -SimpleMatch |
select -expand line |
foreach {
$_ -match '(.+)\s\[(does not exist)\]\s(.+)'| Out-Null
$error_time = [datetime]($matches[1]).split(",")[0]
if ($error_time -gt (Get-Date).AddDays(-2)) {
$content += $_ + "`n"
}
}
cat $logfile |
Select-String "Could not identify speaker!" -SimpleMatch |
select -expand line |
foreach {
$_ -match '(.+)\s\[(Could not identify speaker!)\]\s(.+)'| Out-Null
$error_time = [datetime]($matches[1]).split(",")[0]
if ($error_time -gt (Get-Date).AddDays(-2)) {
$content += $_ + "`n"
}
}
cat $logfile |
Select-String "Warn" -SimpleMatch |
select -expand line |
foreach {
$_ -match '(.+)\s\[(Warn)\]\s(.+)'| Out-Null
$error_time = [datetime]($matches[1]).split(",")[0]
if ($error_time -gt (Get-Date).AddDays(-2)) {
$content += $_ + "`n"
}
}
$content = $content | select -uniq
$file = "c:\temp\shortenedlog.txt"
$content| Add-Content -Path $file
Here's a short take, let me know if this helps or if needs tweaking:
$newlog=#()
$logfile = get-content C:\temp\programlogs.log
$searchFor="error","device","does not exist","Could not identify speaker!","warn"
foreach($line in $logfile){
if($line.Length -gt 18){
$datetime=date $line.substring(0,$line.indexof(","))
if(((date)-$datetime).TotalHours -lt 49){
$keep=$false
foreach($item in $searchFor){
if($line.contains($item)){ $keep=$true }
}
if ($keep){$newlog+=$line}
}
}
}
$newlog | sort | % {add-content C:\temp\NewLog.log $_}
Came up with a solution which I have fed in the days i am interested in, followed by the search criteria. Used this solution as works quickly.
#Set parameters.
$File = "c:\temp\RefinedLogs.txt"
$DateParam = (Get-Date).AddDays(-1).ToString('yyyy-MM-dd')
$DateParam1 = (Get-Date).ToString('yyyy-MM-dd')
$SearchForDate = #("$dateparam", "$dateparam1")
$SearchFor=#("error","device","does not exist","Could not identify speaker!","warn")
#Filter file with dates set in $SearchForDate.
$DateFiltered = Get-Content '.\MyAPP.log' | Select-String -Pattern $SearchForDate -SimpleMatch
#Filter variable for phrases set in $SearchFor.
$Content = $DateFiltered | Select-String -Pattern $SearchFor -SimpleMatch
#Make results readable
ForEach($line in $content){
$Object = "$line" + "`n"
$FinalResult += $Object
}
#Output results.
write-host $FinalResult
I have this script:
# Read in the RESOURCE ID values I want to locate
$TextToFind = Get-Content -Path .\ResourceIDs.txt
$Text = ""
$PathArray = #()
$Results = ".\ResultsResourceIDs.txt"
# Now iterate each of these text values
$TextToFind | ForEach-Object {
$Text = $_
Write-Host "Checking for: " $Text
If ((Get-Content .\Resources.rc) | Select-String -Pattern $Text) {
$PathArray += $Text + "¬Found"
}
Else {
$PathArray += $Text + "¬Not Found"
}
}
Write-Host "Contents of ArrayPath:"
$PathArray | ForEach-Object {$_}
$PathArray | % {$_} | Out-File $Results
It works fine. But the resulting text file has content like:
IDR_ANNOUNCE_TEXT¬Not Found
IDC_BUTTON_UNDO¬Found
IDS_STR_CBS2¬Not Found
Why does it have the strange character?
This is due to encoding, you should use set_content CmdLet and you can play on -encoding param if necessary.
$PathArray | % {$_} | Set-Content $Results -Encoding UTF8