Replace or add .INI file content - powershell

Need to update a .INI file across multiple computers and change the contents. I have the following script that works:
(Get-Content SDA_Apps.ini) | Foreach-Object {
$_ -replace "UserName=.+", "UserName=Test" `
-replace "UserEmail=.+", "UserEmail=test#test.com" `
-replace "UserNo=.+", "UserNo=1234" `
-replace "UserKey=.+", "UserKey=^%&$*$778-" `
-replace "KEM=.+", "KEM=H10"
} | Set-Content SDA_Apps.ini
Sometimes those lines of text do not exist and I need to add the text instead of replace it.
This is my attempt to do this - without success:
function setConfig( $file, $key1, $value1, $key2, $value2 ) {
$content = Get-Content $file
if ( $content -match "^$key\s*=" ) {
$content $_ -replace "^$key1\s*=.*", "$key1=$value1" -replace "^$key2\s*=.*", "$key2=$value2"|
Set-Content $file
} else {
Add-Content $file "$key1 = $value1"
Add-Content $file "$key2 = $value2"
}
}
setConfig "SDA_Apps.ini" "UserName" "Test" "UserEmail" "test#test.com"

I rewrote your function and renamed it to reflect what it actualy does Set-OrAddIniValue:
function Set-OrAddIniValue
{
Param(
[string]$FilePath,
[hashtable]$keyValueList
)
$content = Get-Content $FilePath
$keyValueList.GetEnumerator() | ForEach-Object {
if ($content -match "^$($_.Key)=")
{
$content= $content -replace "^$($_.Key)=(.*)", "$($_.Key)=$($_.Value)"
}
else
{
$content += "$($_.Key)=$($_.Value)"
}
}
$content | Set-Content $FilePath
}
The benefit of this function is that you can pass a key-value list as a hashtable to it. It reads the ini file only once, updates the content and saves it back. Here is an usage example:
Set-OrAddIniValue -FilePath "c:\yourinipath.ini" -keyValueList #{
UserName = "myName"
UserEmail = "myEmail"
UserNewField = "SeemsToWork"
}

Related

PowerShell add string before pattern in a file

I'm trying to add a line in a .sln file before the $pattern. The only problem is that when I try to add the $firstOccurrence condition in the if statement it doesn't add anything at all. It still triggers the Write-Debug.
The first occurrence part is now commented out but I can't seem to find out why it doesn't write anything when I set the first occurrence.
Original source to my solution can be found here:
How to declare a variable and its type is Boolean in PowerShell?
$firstOccurrence = $true;
$pattern = "Global"
(Get-Content $fileName) | Foreach-Object {
#if ($firstOccurrence) {
if ($_ -match $pattern) {
Write-Debug "test"
$firstOccurrence = $false
#Add Lines after the selected pattern
"aaaaaaaaaaaaaaaaaaaaaaa"
}
#}
# send the current line to output
$_
} | Set-Content $fileName
You could also do this using (very fast) switch -Regex like:
$fileName = 'D:\Test\blah.txt'
$firstOccurrence = $true
$pattern = "Global"
$insert = "aaaaaaaaaaaaaaaaaaaaaaa"
$newContent = switch -Regex -File $fileName {
$pattern {
if ($firstOccurrence) {
$insert
$firstOccurrence = $false
}
$_
}
default { $_ }
}
$newContent | Set-Content $fileName -Force
Or did you perhaps mean this:
$fileName = 'D:\Test\blah.txt'
$pattern = "Global"
$insert = "aaaaaaaaaaaaaaaaaaaaaaa"
((Get-Content -Path $fileName -Raw) -split $pattern, 2) -join "$insert $pattern" | Set-Content $fileName -Force
?

How to modify Firefox config (pref.js) line using Powershell

Modifying the Mozilla Firefox Pref.js file using PowerShell.
Need to modify the pref.js file line to reflect a new value
(Hence, instead of any value under user_pref("network.automatic-ntlm-auth.trusted-uris", "*"); to show user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com") or just add changes after the *
$TopDir = "$env:APPDATA\Mozilla\Firefox\Profiles"
$FileName = 'prefs.js'
$DefaultProfileDir = (Get-ChildItem -LiteralPath $TopDir -Directory).
Where({
$_.FullName -match '\.default'
}).FullName
$FullFileName = Join-Path -Path $DefaultProfileDir -ChildPath $FileName
$data = foreach($line in Get-Content $FullFileName)
{
if($line -contains 'user_pref("network.automatic-ntlm-auth.trusted-uris".*')
{
$line -replace '*' , 'user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com");'
}
else
{
$line
}
}
$data | Set-Content C:\testfolder\test2.txt
Nelson,
I didn't have your line in my FF profile but I got the code to work on another line so you should be able to modify it to work with yours.
$TopDir = "G:\Test"
$FileName = 'prefs.js'
$FullFileName = "$TopDir\$FileName"
Clear-Host
$Data = foreach($line in Get-Content $FullFileName) {
if( ($line.IndexOf('browser.bookmarks.restore_default_bookmarks')) -ne -1)
{
If ($line.IndexOf('false') -ne -1) {
$line = $line.replace( 'false' , 'true')
}
}
$line
}
$data | Set-Content $("$TopDir" + "\prefs2.js")
I think your code would be:
$TopDir = "$env:APPDATA\Mozilla\Firefox\Profiles"
$FileName = 'prefs.js'
$DefaultProfileDir = (Get-ChildItem -LiteralPath $TopDir -Directory).
Where({
$_.FullName -match '\.default'
}).FullName
$FullFileName = Join-Path -Path $DefaultProfileDir -ChildPath $FileName
$Data = foreach($line in Get-Content $FullFileName) {
if( ($line.IndexOf('network.automatic-ntlm-auth.trusted-uris')) -ne -1)
{
If ($line.IndexOf('*') -ne -1) {
$line = $line.replace( '*' , 'abc.com,.abcd.com,.abcde.com')
}
}
$line
}
$data | Set-Content C:\testfolder\test2.txt
Note: -contains is designed for lists.
HTH
I extended it to be easier to use any property
Input
user_pref("foo", "bar")
user_pref("network.automatic-ntlm-auth.trusted-uris", "*")
Output
user_pref("foo", "bar")
user_pref("network.automatic-ntlm-auth.trusted-uris", ".abc.com,.abcd.com,.abcde.com")
$lines = 'user_pref("foo", "bar")', 'user_pref("network.automatic-ntlm-auth.trusted-uris", "*")'
# multi-line regex for readability
$regex = '(?x)
(?<prefix>user_pref\()
(?<name>
".*"
)
\s*,\s*
(?<value>
".*"
)
(?<suffix>.*)
'
"`ninput:"
$lines
"`noutput:"
$lines | ForEach-Object {
if($_ -match $regex) {
if($matches.name -eq '"network.automatic-ntlm-auth.trusted-uris"') {
$_ = '{0}{1}, {2}{3}' -f #(
$Matches.prefix,
$Matches.name,
'".abc.com,.abcd.com,.abcde.com"',
$Matches.suffix
)
}
}
$_
}

How to edit INI file value in Powershell?

I have an INI file:
[Name]
Female = 10
Male = 30
[Class]
Kids = 2
Adult = 10
I want to change the value of each section. Give me idea please.
I tried this code:
function Set-OrAddIniValue
{
Param(
[string]$FilePath,
[hashtable]$keyValueList
)
$content = Get-Content $FilePath
$keyValueList.GetEnumerator() | ForEach-Object {
if ($content -match "^$($_.Key)=")
{
$content= $content -replace "^$($_.Key)=(.*)", "$($_.Key)=$($_.Value)"
}
else
{
$content += "$($_.Key)=$($_.Value)"
}
}
$content | Set-Content $FilePath
}
this is the code
You would do so:
$ini = Get-IniContent "<pathToYourIniFile>"
$ini["<yourSection>"]["<yourKey>"] = "<yourValue>"
$ini | out-inifile -FilePath "<pathToYourIniFile>"
If you updated your code to the following, it will be more successful:
function Set-OrAddIniValue
{
Param(
[string]$FilePath,
[hashtable]$keyValueList
)
$content = Get-Content $FilePath
$keyValueList.GetEnumerator() | ForEach-Object {
if ($content -match "$($_.Key)\s*=")
{
$content= $content -replace "$($_.Key)\s*=(.*)", "$($_.Key)=$($_.Value)"
}
else
{
$content += "$($_.Key)=$($_.Value)"
}
}
$content | Set-Content $FilePath
}
One problem will be if you add extra key/value pairs, they will all be added to the last section. In other words, the code is not section-aware.

Issue with nested powershell

I have this code snippet where I try tro replace some strings in all files of a directory. I thought I could nest the foreach in the ForEach-Object, but this does not seem to work.
The error I get is:
InvalidArgument: (:) [ForEach-Object], ParameterBindingException
$files = Get-ChildItem $testdir\reference *.* -recurse
$replacementMap = #{"Fruit::Apple" = "NewApple";"Fruit::Banana" = "NewBanana"}
foreach ($file in $files)
{
If (Get-Content $($file.FullName) | Select-String -Pattern "Fruit::")
{
$content = Get-Content $($file.FullName) | ForEach-Object
{
$line = $_
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
}
$content = $content -join "`r`n"
$content | Set-Content $($file.FullName)
}
This code worked without the
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
part. Anyone has a clue what I'm doing wrong? Thanks in advance
You missed a curly brace closure and formatting issue on the foreach-object. You need to take care of foreach and foreach-object in a different way:
Replace your existing foreach part with this:
foreach ($file in $files)
{
If(Get-Content $($file.FullName) | Select-String -Pattern "Fruit::")
{
$content = Get-Content $($file.FullName) | %{
$line = $_
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
}
$content = $content -join "`r`n"
$content | Set-Content $($file.FullName)
}
}
Instead of processing the files line-wise, just do the replacement operation on the entire file content at once. If the content has changed, overwrite the file.
$replacementMap = #{
"Fruit::Apple" = "NewApple"
"Fruit::Banana" = "NewBanana"
}
Get-ChildItem $testdir\reference -File -Recurse | foreach {
$content = Get-Content $_
$dirty = $false
foreach ($key in $replacementMap.Keys) {
$content = $content -replace $key,$replacementMap.$key
$dirty = $true
}
if ($dirty) { $content | Set-Content $_ }
}

Perform function for all c:\users\*\AppData\Local

Got the following script that I thought would happily update the specified .ini file in each C:\users\*\AppData\Local\Greeentram folder individually.
function Set-OrAddIniValue {
Param(
[string]$FilePath,
[hashtable]$keyValueList
)
$content = Get-Content $FilePath
$keyValueList.GetEnumerator() | ForEach-Object {
if ($content -match "^$($_.Key)=") {
$content= $content -replace "^$($_.Key)=(.*)", "$($_.Key)=$($_.Value)"
} else {
$content += "$($_.Key)=$($_.Value)"
}
}
$content | Set-Content $FilePath
}
Set-OrAddIniValue -FilePath "C:\Users\*\AppData\Local\Greentram\SDA_Apps.ini" -keyValueList #{
UserName = "Dcebtcv7[[G"
UserEmail = "x}tpwpjmkxmvkYjmklzmx7zv7lr"
UserNo = "*++*(+"
UserKey = "^X(_0[*_/0L)\_0,U,-"
KEM = "H10"
}
What it seems to be doing is somehow combining all the .INI files together and creating a new .INI file for each user.
I have wrongly assumed that C:\Users\*\AppData\Local\Greentram\SDA_Apps.ini would work.
I only want to update or add these specific values to each .INI file.
Set-OrAddIniValue -FilePath "C:\Users\*\AppData\Local\Greentram\SDA_Apps.ini" -keyValueList #{
UserName = "Dcebtcv7[[G"
UserEmail = "x}tpwpjmkxmvkYjmklzmx7zv7lr"
UserNo = "*++*(+"
UserKey = "^X(_0[*_/0L)\_0,U,-"
KEM = "H10"
}
Your function Set-OrAddIniValue doesn't handle wildcards in paths.
$content = Get-Content $FilePath
...
$content | Set-Content $FilePath
The first statement reads the content of all matching files into a single array. The second statement then writes the entire modified content to all matching files. (How would it decide which content belongs to which file?)
You can either call your function for each file individually:
Get-ChildItem "C:\Users\*\AppData\Local\Greentram\SDA_Apps.ini" | ForEach-Object {
Set-OrAddIniValue -FilePath $_.FullName -keyValueList ...
}
or change your function so that it does the enumeration internally:
function Set-OrAddIniValue {
Param(
[string]$FilePath,
[hashtable]$keyValueList
)
Get-ChildItem $FilePath | Where-Object {
-not $_.PSIsContainer # process only files
} | ForEach-Object {
$file = $_.FullName
$content = Get-Content $file
...
$content | Set-Content $file
}
}
On PowerShell v3 and newer you can use Get-ChildItem -File instead of piping the object list through Where-Object {-not $_.PSIsContainer}.