Replacing value on one line in a text file - string

I am currently working on editing one line of a text file. When I try to overwrite the text file, I only get one line back in the text file. I am trying to call the function with
modifyconfig "test" "100"
config.txt:
check=0
test=1
modifyConfig() function:
Function modifyConfig ([string]$key, [int]$value){
$path = "D:\RenameScript\config.txt"
((Get-Content $path) | ForEach-Object {
Write-Host $_
# If '=' is found, check key
if ($_.Contains("=")){
# If key matches, replace old value with new value and break out of loop
$pos = $_.IndexOf("=")
$checkKey = $_.Substring(0, $pos)
if ($checkKey -eq $key){
$oldValue = $_.Substring($pos+1)
Write-Host 'Key: ' $checkKey
Write-Host 'Old Value: ' $oldValue
$_.replace($oldValue,$value)
Write-Host "Result:" $_
}
} else {
# Do nothing
}
}) | Set-Content ($path)
}
The result I receive in my config.txt:
test=100
I am missing "check=0".
What have I missed?

$_.replace($oldValue,$value) in your innermost conditional replaces $oldValue with $value and then prints the modified string, but you don't have code printing non-matching strings. Because of that only the modified string are written back to $path.
Replace the line
# Do nothing
with
$_
and also add an else branch with a $_ to the inner conditional.
Or you could assign $_ to another variable and modify your code like this:
Foreach-Object {
$line = $_
if ($line -like "*=*") {
$arr = $line -split "=", 2
if ($arr[0].Trim() -eq $key) {
$arr[1] = $value
$line = $arr -join "="
}
}
$line
}

or a one liner.. (not exactly pin pointed answer, but to the question title)
(get-content $influxconf | foreach-object {$_ -replace "# auth-enabled = false" , "auth-enabled = true" }) | Set-Content $influxconf

Related

Replacing a substring inside a string in powershell script

I have long text files, containing markup for another software. The form of the text is this:
*INIT=D:\ws\**randomnonesense*
ROW=THISISROW,D:\ws\morestuff
PALVELU=200,WSTIME70.DLL,N,A
PALVELU=201,WSLDIR70.DLL,N,A
PALVELU=202,WSLDIX70.DLL,N,A
PALVELU=204,WSEXCE32.DLL,N,A
PALVELU=205,WMON.DLL,N,A
PALVELU=206,WSWORD32.DLL,N,A
PALVELU=207,WSLEPT32.DLL,N,A
PALVELU=208,WSCONV70.DLL,N,A
PALVELU=209,WSFTPC70.DLL,N,A
KUVAUS=\\192.168.169.17\adwise$\applic\LIKSA_TURE.A70,D:\ws\%aspno%\%username%
MDBS-KANTA=LIKSAV,%aspno%LIK.DB,5,RTTL,ANSI,111.111.111.11MDBS-
KANTA=LANKA,%aspno%LAN.DB,5,RTTL,ANSI,1000.000.111.11
I am writing a script, that needs to replace all of those rows that start with the word PALVELU. The amount of other stuff before and after those rows can be any. Also number of those PALVELU-rows or their lenght is not the same in every file. Still I would need to replace all of those rows in every file with another set of rows. Is there a way to do this?
You can use the following method:
Replace(oldStr, newStr)
eg:
Write-Host "replacing a text in string"
$test=" old text to be replaced"
Write-Host "going to replace old"
$test.Replace("old","New")
Write-Host "Text is replaced"
Hope this will helpful.
You can use [regex]::split to split content of your file by lines, after you have all line you can check if each line contain the word PALVELU using -like operator.
I use \n in the regex pattern to split your content on every start of a newline
$rows = [regex]::split("*INIT=D:\ws\**randomnonesense*
ROW=THISISROW,D:\ws\morestuff
PALVELU=200,WSTIME70.DLL,N,A
PALVELU=201,WSLDIR70.DLL,N,A
PALVELU=202,WSLDIX70.DLL,N,A
PALVELU=204,WSEXCE32.DLL,N,A
PALVELU=205,WMON.DLL,N,A
PALVELU=206,WSWORD32.DLL,N,A
PALVELU=207,WSLEPT32.DLL,N,A
PALVELU=208,WSCONV70.DLL,N,A
PALVELU=209,WSFTPC70.DLL,N,A
KUVAUS=\\192.168.169.17\adwise$\applic\LIKSA_TURE.A70,D:\ws\%aspno%\%username%
MDBS-KANTA=LIKSAV,%aspno%LIK.DB,5,RTTL,ANSI,111.111.111.11MDBS-
KANTA=LANKA,%aspno%LAN.DB,5,RTTL,ANSI,1000.000.111.11", "(.*)\n")
$output = [System.Collections.ArrayList]#()
foreach($row in $rows) {
if($row -like '*PALVELU*') {
$output.Add($row.Replace("PALVELU", "TEST")) | Out-Null
} else {
$output.Add($row) | Out-Null
}
}
If the goal is to replace each PALVELU row with a new row, you may do the following:
# Assumes your file is a.txt
$ReplaceThis = '^PALVELU.*$'
$ReplaceWith = '<new row>'
$(switch -regex -file a.txt {
$ReplaceThis { $_ -replace $ReplaceThis,$ReplaceWith }
Default { $_ }
}) | Set-Content a.txt
The code above will result in X number of PALVELU rows being replaced with X number of <new row>.
If the goal is to replace consecutive PALVELU rows with a single <new row> string in files that only contain one set of consecutive PALVELU rows, you may do the following:
$ReplaceThis = '(?sm)^PALVELU.*(?-s)^PALVELU.*$'
$ReplaceWith = '<new row>'
(Get-Content a.txt -Raw) -replace $ReplaceThis,$ReplaceWith |
Set-Content a.txt
If the goal is to replace all PALVELU rows with a single <new row> string at the location of the first PALVELU row and the rows could be anywhere in the file, you may do the following:
$ReplaceThis = '^PALVELU.*$'
$ReplaceWith = '<new row>'
$firstMatch = $false
$(switch -regex -file a.txt {
$ReplaceThis {
if ($firstMatch) {
$_ -replace $ReplaceThis
}
else {
$_ -replace $ReplaceThis,$ReplaceWith
$firstMatch = $true
}
}
default { $_ }
}) -ne '' | Set-Content a.txt

Replacing multiple texts not getting saved

I've a bunch of files in which I need to replace content like for e.g. wherever there is 'AA' I need to replace with 'E1', 'A1' with 'P4'. The same content needs to be changed differently in different files. So for example in the 2nd file 'AA' would become 'P1', 'A1' would become 'E1', etc. To accomplish this I've an Excel sheet with 2 columns like the below:
TC CodeChange
086 AA-E1; A1-P2
099 AA-P2; A1-E1; A2-E2; Z3-E3
100 AA-P2; A1-E2; A2-E3; Z3-O3
PowerShell script which I wrote for the above:
Script 1:
function func3 {
Param($arr3, $pat)
$arr3.GetEnumerator() | ?{$_.key -like $pat} | ForEach-Object {
$output = $_.value
return $output
}
}
$src = "C:\...xlsx"
$src1 = "C:\...\..."
$sheetName = "Sheet1"
$arr = #{};
$objExcel = New-Object -ComObject Excel.Application
$workbook = $objExcel.Workbooks.Open($src)
$sheet = $workbook.Worksheets.Item($sheetName)
$objExcel.Visible = $false
$rowMax = ($sheet.UsedRange.Rows).count
$rowTC, $colTC = 1, 1
$rowCodeChange, $colCodeChange = 1, 2
for ($i=1; $i -le $rowMax-1; $i++) {
$TC = $sheet.Cells.Item($rowTC+$i, $colTC).Text
$CodeChg = [String]($sheet.Cells.Item($rowCodeChange+$i, $colCodeChange).Text)
if ($arr.ContainsKey($TC) -eq $false) {
$arr.Add($TC, $CodeChg)
}
}
$inputfiles = (Get-ChildItem -Path $src1 -Recurse)
foreach ($inputfile in $inputfiles) {
$pat1 = $inputfile.Name.SubString(8, 3)
$val = func3 $arr $pat1
$arry1 = $val -split ';'
Write-Host $arry1.Length
$j = 0
do {
#skipping these 3 items from getting replaced
if (($arry1[$j].Trim() -ne "S1") -and ($arry1[$j].Trim() -ne "S2") -and ($arry1[$j].Trim() -ne "S3")) {
(Get-Content $inputfile.FullName) | ForEach-Object {
$_ -replace "$($arry1[$j].Split('-')[0])","$($arry1[$j].Split('-')[1])"
} | Set-Content $inputfile.FullName
}
$j++
} while ($j -le ($arry1.Length-1))
}
$objExcel.Quit()
Script 2:
function func3 {
param($arr3, $pat)
$arr3.GetEnumerator() | ?{$_.key -like $pat} | ForEach-Object {
$output=$_.value
return $output
}
}
$src = "C:\...xlsx"
$src1 = "C:\..."
$sheetName = "Sheet1"
$arr = #{};
$objExcel = New-Object -ComObject Excel.Application
$workbook = $objExcel.Workbooks.Open($src)
$sheet = $workbook.Worksheets.Item($sheetName)
$objExcel.Visible = $false
$rowMax = ($sheet.UsedRange.Rows).Count
$rowTC, $colTC = 1, 1
$rowCodeChange, $colCodeChange = 1, 2
for ($i=1; $i -le $rowMax-1; $i++) {
$TC = $sheet.Cells.Item($rowTC+$i, $colTC).Text
$CodeChg = [String]($sheet.Cells.Item($rowCodeChange+$i, $colCodeChange).Text)
if ($arr.ContainsKey($TC) -eq $false) {
$arr.Add($TC, $CodeChg)
}
}
$inputfiles = (Get-ChildItem -Path $src1 -Recurse)
foreach ($inputfile in $inputfiles) {
$pat1 = $inputfile.Name.SubString(8, 3)
$val = func3 $arr $pat1
$arry1 = $val -split ';'
Write-Host $arry1.Length
$j = 0
do {
#skipping these 3 items from getting replaced
if (($arry1[$j].Trim() -ne "S1") -and ($arry1[$j].Trim() -ne "S2") -and ($arry1[$j].Trim() -ne "S3")){
$content = [System.IO.File]::ReadAllText($inputfile.FullName).Replace($arry1[$j].Split('-')[0], $arry1[$j].Split('-')[1])
[System.IO.File]::WriteAllText($inputfile.FullName, $content)
Write-Host $arry1[$j].Split('-')[0]' replaced with '$arry1[$j].Split('-')[1]' in file: '$inputfile.FullName
}
$j++
} while ($j -le ($arry1.Length-1))
}
$objExcel.Quit()
The folder where the files are has the files having names containing the same digits in the 'TC' column in my Excel sheet. Example:
TC 086.txt
TC 099.txt
etc.
That way after I import the contents of the Excel into a hashtable I extract the digits from the filenames and get the corresponding values for the same key in the hashtable. For example the value for the key '086' from the hashtable would be 'AA-E1; A1-P2'. Then I split the items to be replaced from the hashtable value (separated by ;) and then store that in an array. The using a loop I try to replace the contents of each file based on the data retrieved from the spreadsheet.
The issue I'm facing with both the approaches is that only the 1st item in each file is getting replaced. The rest of the items are not getting replaced. For example only 'AA' value in file 'TC 086.txt' is getting replaced with 'E1'. 'A1' is not getting replaced with 'P2'.
I found out what the issue was. I basically had to trim the elements of the array
$arry1
after splitting them (separated by ;) and before passing them as parameters to the 'Replace' function. Apparently there was a space before every element in that array except the 1st element (that's how they were stored in the source: excel spreadsheet). Hence the 'Replace' method was not finding that element in the file and hence not replacing it. Removing the spaces before the elements solved the issue

Efficiently search a string in large files

How can I check if a string exists in:
1 text file;
size up until 10GB;
taking into account that the file is only one line;
the file only contains random numbers 1 to 9;
using powershell (because I think it will be more efficient, although I don't know how to program in this language);
I have tried this in batch:
FINDSTR "897516" decimal_output.txt
pause
But as I said I need the faster and more efficient way to do this.
I also tried this code that I have found in stackoverflow:
$SEL = Select-String -Path C:\Users\fabio\Desktop\CONVERTIDOS\dec_output.txt -Pattern "123456"
if ($SEL -ne $null)
{
echo Contains String
}
else
{
echo Not Contains String
}
But I get the error below, and I don't know if this code is the most solid or adequate. The error:
Select-String : Tipo de excepção 'System.OutOfMemoryException' accionado.
At C:\Users\fabio\Desktop\1.ps1:1 char:8
+ $SEL = Select-String -Path C:\Users\fabio\Desktop\CONVERTIDOS\dec_out ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Select-String], OutOfMemoryException
+ FullyQualifiedErrorId : System.OutOfMemoryException,Microsoft.PowerShell.Commands.SelectStringCommand
This should do the job:
#################################################################################################################
#
# Searches for a user defined string in the $input_file and counts matches. Works with files of any size.
#
# Adjust source directory and input file name.
#
$source = "C:\adjust\path"
$input_file = "file_name.extension"
#
#
# Define the string you want to search for. Keep quotation marks even if you only search for numbers (otherwise
# $pattern.Length will be 1 and this script will no longer work with files larger than the $split_size)!
#
$pattern = "Enter the string to search for in here"
#
#
# Using Get-Content on an input file with a size of 1GB or more will cause System.OutOfMemoryExceptions,
# therefore a large file gets temporarily split up.
#
$split_size = 100MB
#
#
# Thanks #Bob (https://superuser.com/a/1295082/868077)
#################################################################################################################
Set-Location $source
if (test-path ".\_split") {
while ($overwrite -ne "true" -and $overwrite -ne "false") {
"`n"
$overwrite = Read-Host ' Splitted files already/still exist! Delete and overwrite?'
if ($overwrite -match "y") {
$overwrite = "true"
Remove-Item .\_split -force -recurse
$a = "`n Deleted existing splitted files!"
} elseif ($overwrite -match "n") {
$overwrite = "false"
$a = "`n Continuing with existing splitted files!"
} elseif ($overwrite -match "c") {
exit
} else {
Write-Host "`n Error: Invalid input!`n Type 'y' for 'yes'. Type 'n' for 'no'. Type 'c' for 'cancel'. `n`n`n"
}
}
}
Clear-Host
if ((Get-Item $input_file).Length -gt $split_size) {
while ($delete -ne "true" -and $delete -ne "false") {
"`n"
$delete = Read-Host ' Delete splitted files afterwards?'
if ($delete -match "y") {
$delete = "true"
$b = "`n Splitted files will be deleted afterwards!"
} elseif ($delete -match "n") {
$delete = "false"
$b = "`n Splitted files will not be deleted afterwards!"
} elseif ($delete -match "c") {
exit
} else {
Write-Host "`n Error: Invalid input!`n Type 'y' for 'yes'. Type 'n' for 'no'. Type 'c' for 'cancel'. `n`n`n"
}
}
Clear-Host
$a
$b
Write-Host `n This may take some time!
if ($overwrite -ne "false") {
New-Item -ItemType directory -Path ".\_split" >$null 2>&1
[Environment]::CurrentDirectory = Get-Location
$bytes = New-Object byte[] 4096
$in_file = [System.IO.File]::OpenRead($input_file)
$file_count = 0
$finished = $false
while (!$finished) {
$file_count++
$bytes_to_read = $split_size
$out_file = New-Object System.IO.FileStream ".\_split\_split_$file_count.splt",CreateNew,Write,None
while ($bytes_to_read) {
$bytes_read = $in_file.Read($bytes, 0, [Math]::Min($bytes.Length, $bytes_to_read))
if (!$bytes_read) {
$finished = $true
break
}
$bytes_to_read -= $bytes_read
$out_file.Write($bytes, 0, $bytes_read)
}
$out_file.Dispose()
}
$in_file.Dispose()
}
$i++
while (Test-Path ".\_split\_split_$i.splt") {
$cur_file = (Get-Content ".\_split\_split_$i.splt")
$temp_count = ([regex]::Matches($cur_file, "$pattern")).Count
$match_count += $temp_count
$n = $i - 1
if (Test-Path ".\_split\_split_$n.splt") {
if ($cur_file.Length -ge $pattern.Length) {
$file_transition = $prev_file.Substring($prev_file.Length - ($pattern.Length - 1)) + $cur_file.Substring(0,($pattern.Length - 1))
} else {
$file_transition = $prev_file.Substring($prev_file.Length - ($pattern.Length - 1)) + $cur_file
}
$temp_count = ([regex]::Matches($file_transition, "$pattern")).Count
$match_count += $temp_count
}
$prev_file = $cur_file
$i++
}
} else {
$a
$match_count = ([regex]::Matches($input_file, "$pattern")).Count
}
if ($delete -eq "true") {
Remove-Item ".\_split" -Force -Recurse
}
if ($match_count -ge 1) {
Write-Host "`n`n String '$pattern' found:`n`n $match_count matches!"
} else {
Write-Host "`n`n String '$pattern' not found!"
}
Write-Host `n`n`n`n`n
Pause
This will split a large file into mutliple smaller files, search them for $pattern and count the matches (taking file transitions into account).
It also offers you to delete or keep the splitted files afterwards so you can reuse them and don't have to split the large file every time you run this script.

PowerShell set each string part as a variable for reuse

I have a list of files in a folder each are in this format: custID_invID_prodID or custID_invID_prodID_Boolvalue. For every file I need to break it into sections based on '_'. Currently I have this code:
$files = Get-ChildItem test *.txt
foreach($f in $files){
$file = #()
$file += ([String]$f).Split("_")
$total = ([String]$f).Split("_") | Measure-Object | select count
Write-Host "${total}"
if($total -eq 2) {
for($i = 2; $i -lt $file.length; $i+=3) {
$file[$i] = $file[$i].trimend(".txt")
Write-Host "${file}"
}
}
}
The problem is that Write-Host "${total}" equals #{Count=#} where # is real number of times "_" is found in file. How can I use $total inside my if statement to do different operations based upon the number of "_" found?
Would it not be simpler just to assign the parts you want directly to named variables rather than working with an array?
foreach($f in (Get-ChildItem test *.txt)) {
$custId, $invID, $prodID, $Boolvalue = $f.BaseName -split "_"
Write-Host $custId, $invID, $prodID, $Boolvalue
}
If the name only has 3 parts this will simply set $Boolvalue to an empty string.
Also note that you don't have to trim the extension off the last element after splitting, just use the BaseName property to get the name without extension.
You need to get the count-property value, like $total.count in your if test. You could also clean it up like this.
$files = Get-ChildItem test *.txt
foreach($f in $files){
$file = #(([String]$f).Split("_"))
Write-Host "$($file.Count)"
if($file.Count -eq 2) {
for($i = 2; $i -lt $file.length; $i+=3) {
$file[$i] = $file[$i].trimend(".txt")
Write-Host "${file}"
}
}
}
If you had included more information about what you were trying to do, we could clean it up alot more. Ex. It's seems like you want to do something like this:
Get-ChildItem test *.txt | ForEach-Object {
$file = #($_.BaseName.Split("_"))
Write-Host "$($file.Count)"
if($file.Count -eq 2) {
Write-Host $file
}
}
Seems to me that you're doing it the hard way. Why not:
$x = "aaa_bbb_ccc"
$cnt = $x.Split("_").count

Powershell filter a List by Name and Date

I need a bit of help... I'm new to powershell and i want to Filter a List (csv). I would love to remove all lines with certain names in it. and cut the list down to the last month. In the script you can see how far i got till now.
param(
[Parameter(ValueFromPipeline=$true,HelpMessage="Enter CSV path(s)")]
[String[]]$Path = $null
)
if($Path -eq $null) {
Add-Type -AssemblyName System.Windows.Forms
$Dialog = New-Object System.Windows.Forms.OpenFileDialog
$Dialog.InitialDirectory = "$InitialDirectory"
$Dialog.Title = "Select CSV File(s)"
$Dialog.Filter = "CSV File(s)|*.csv"
$Dialog.Multiselect=$true
$Result = $Dialog.ShowDialog()
if($Result -eq 'OK') {
Try {
$Path = $Dialog.FileNames
}
Catch {
$Path = $null
Break
}
}
else {
Write-Host -ForegroundColor Yellow "Notice: No file(s) selected."
Break
}
}
$info=Import-Csv "$path" -Delimiter ';'
$info | Get-Member
$info | Format-Table
as you can see i tryed to link the path to a filebrowser.
For the purposes of discussion, I will assume that the full pathname of the CSV is in the variable $InputPath, and that you want to write the result to a CSV file whose full pathname is in the variable $OutputPath. I will also assume that the CSV file contains a column named 'Name', and that the value from the Name column that you want to exclude is in the variable $ExcludedName. Given that, you can simply do
Import-CSV -Path $InputPath | Where-Object {$_.Name -ne $ExcludedName} | Export-CSV -Path $OutputPath -NoTypeInformation
You can do this by my code,but dont forget that first row must contains names of column and delimiter must be ';' and $nameslist is array of names that you need delete:
$info=Import-Csv "D:\testdir\file2.csv" -Delimiter ';'
$nameslist=#('James','John','andrew')
foreach($i in $info){
if($nameslist -contains $i.Name){
$i.Name=""
}
$i|Export-Csv -Path "D:\testdir\file1.csv" -Delimiter ';' -NoTypeInformation -Force -Encoding UTF8 -Append
}
Try this:
$data = Import-Csv "Path" | Select-Object * -ExcludeProperty Names
$data | export-csv "Path" -Notype
This will cut the column names.
Try it first without using a function:
Import-Csv <Filename> | Where-Object {$_.<FieldName> -notlike "*<Value>*"}
Also, you might consider something like this:
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true, HelpMessage = "Enter CSV path(s)")]
[String[]]$Path = $(
Add-Type -AssemblyName System.Windows.Forms
$DialogProperties = #{
Title = 'Select CSV File(s)'
Filter = 'CSV File(s)|*.csv'
Multiselect = $True
}
$Dialog = New-Object System.Windows.Forms.OpenFileDialog -Property $DialogProperties
$Dialog.ShowDialog()
If ($Result -eq 'OK') {
$Path = $Dialog.FileNames
} Else {
Write-Error 'Notice: No file(s) selected.'
}
)
)
Process {
ForEach ($PathItem in $Path) {
Import-Csv $PathItem | Where-Object { $_.Name -notlike "*NotThisOne*" }
}
}

Resources