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
?
Related
As newby to powershell I googled a long time to make a piece that can remove a part of text in a single file based upon some variables like this. The text section can be anywhere within the file. And that specific section needs to be removed.
<#
#create test file
$file = "c:\users\public\Test\remove.txt"
Set-Content $file (1..100) -Force
$content = Get-Content $file
#>
$file = Get-Content "c:\users\public\Test\remove.txt"
$StartText= '50'
$EndText= "75"
$LineNumberStart= $file | Select-string -Pattern $StartText
$Start = $linenumberStart.LineNumber
$LineNumberEnd= $file | Select-string -Pattern $EndText
$End = $linenumberEnd.LineNumber
$keep = $file[0..$Start] + $file[$End..($file.Count - 1)]
$keep | Set-Content "c:\users\public\test\remove.txt"
Now I would like have to the above working functionality but on all files in a particular folder. However, for the reason "Expressions are only allowed as the first element of a pipeline." I can't get it to work with this piece of code below:
$ReportPath = Split-Path -Parent $Script:MyInvocation.MyCommand.Path
$StartText= "50"
$EndText= "75"
Get-ChildItem -Path $ReportPath -Filter *.txt | ForEach { (Get-Content $_.PSPath) |
$LineNumberStart= $_ | Select-string -Pattern $StartText
$Start = $LineNumberStart.LineNumber
$LineNumberEnd= $_ | Select-string -Pattern $EndText
$End = $LineNumberEnd.LineNumber
$keep = $_[0..$Start] + $_[$End..($_.Count - 1)]
$keep|Set-Content $_.PSPath
}
Expected outcome is that all files in the folder have the middle section of the text file removed.
Can someone please assist in getting this foreach construction resolved?
I would recommend you to use a switch for this use case, if I understand correctly you're looking to start skipping the lines of a file from where the line matches the value of $StartText and stop skipping after the line matches the value of $EndText. If that's the case, here is a minimal example of how you can do this using a switch statement with the -Regex parameter:
$StartText = '50'
$EndText = '75'
$skip = $false
switch -Regex (0..100) {
$StartText { $skip = $true; continue }
$EndText { $skip = $false; continue }
{ $skip } { continue }
default { $_ }
}
If this is what you expected, then if you want to the same for each file, the code would look like this, note the use of -File to read each file content:
$StartText = '50'
$EndText = '75'
$skip = $false
Get-ChildItem -Path $ReportPath -Filter *.txt | ForEach-Object {
& {
switch -Regex -File ($_.FullName) {
$StartText { $skip = $true; continue }
$EndText { $skip = $false; continue }
{ $skip } { continue }
default { $_ }
}
} | Set-Content ($_.BaseName + '-New' + $_.Extension)
}
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
)
}
}
$_
}
Here's a method for deleting a line that I thought would work
#earlier in the script
$inFile = Get-Content -Path ".\input.txt"
# ...Later... #
$inFile = Get-Content -path ".\input.txt" | where-object {$_ -notmatch $line}
set-content -path ".\input.txt" -Value $inFile
The problem is that the -notmatch parameter doesn't seem to work. The Get-Content cmdlet just copies all the content from input.txt, including $line. I've also tried changing the code to clear $inFile completley and create a temporary holder, but no dice.
Clear-Variable -name "inFile"
$holder = Get-Content -path ".\input.txt" | where-object {$_ -notmatch $line}
set-content -path ".\input.txt" -Value $holder
$inFile = Get-Content -path ".\input.txt"
Am I using -notmatch incorrectly? Here's the full text script for context.
Write-Host "Starting"
[bool] $keepRunning = 1
[bool] $everFound = 0
[bool] $searchComplete = 0
:main while($keepRunning)
{
$inFile = Get-Content -path ".\input.txt"
$completed = Get-Content -Path ".\output.txt"
$line = $inFile[0]
$holder
if($inFile.count -eq 1)
{
$line = $inFile
}
# create condition to check if $line matches any line in completed.txt
# if it does, skip this line and move on to the next line
:search while($everFound -eq 0 -and $searchComplete -eq 0)
{
#Write-Host "Outer loop"
foreach($url in $completed)
{
#Write-Host $line
#write-host $url
if ($line -eq $url)
{
Write-Host "`nThis file was already downloaded --Skipping to the next line"
$inFile = Get-Content -path ".\input.txt" | where-object {$_ -notmatch $line}
set-content -path ".\input.txt" -Value $inFile
$inFile = Get-Content -path ".\input.txt"
$line = $inFile[0]
$everFound = 1
break
}
}
if ($everFound -eq 1)
{
break
}
$searchComplete = 1
Write-Host "Search Complete`n"
}
Write-Host "Before the download--------"
Write-Host $everFound
Write-Host $searchComplete
if ($everFound -eq 0 -and $searchComplete -eq 1)
{
#download the files
$downloadCommand = "youtube-dl.exe --verbose --cookies .\cookies.txt `"$line`""
get-date
invoke-Expression $downloadCommand
#delete the url
add-content -Path ".\output.txt" -Value $line
$inFile = Get-Content -path ".\input.txt" | where-object {$_ -notmatch $line}
set-content -path ".\input.txt" -Value $inFile
write-host "`n"
get-date
Write-Host "Sleeping for 45mins"
#start-sleep -s 2700
}
$everFound = 0
$searchComplete = 0
Write-Host "-------------After the download!!"
Write-Host $everFound
Write-Host $searchComplete
# check if the file is empty. If it is, set the keepRunning flag to false and exit the main while loop
if($Null -eq $inFile)
{
$keepRunning = 0
}
}
Write-Host "Done"
Read-Host "Press the Enter Key to Exit"
EDIT:
$inFile contains a list of youtube URLs on each line. $line is assigned the value of the first line of $inFile
$line = $inFile[0]
Here is a youtube URL: https://www.youtube.com/watch?v=sB5zlHMsM7k
I also added some statements to output the values of $line right before the file. Someone please point me to the right direction.
Am I using -notmatch incorrectly?
You're using it incorrectly, if $line contains a substring to search for literally (as-is, verbatim) in the input file's lines, and that substring happens to contain regex metacharacters, such as . and $.
To use -match / -notmatch for literal substring matching, you must escape the substring:
$_ -notmatch [regex]::Escape($line)
If you want to match lines only in full, you must anchor the regex:
$_ -notmatch ('^' + [regex]::Escape($line) + '$')
Note that PowerShell has no operator for literal substring matching.
However, the System.String ([string]) type has a .Contains() method for literal substring matching, but not that, unlike PowerShell's operators, it is case-sensitive by default (there are overloads for case-insensitive matching, but only in PowerShell (Core) 7+):
-not $_.Contains($line) # case-sensitive, literal substring matching
# PS 7+ only: case-INsensitive, literal substring matching
-not $_.Contains($line, 'CurrentCultureIgnoreCase')
For full-line matching:
-not ($_.Length -eq $line.Length -and $_.Contains($line))
or:
-not $_.Equals($line, 'CurrentCultureIgnoreCase')
The advantage of using .Contains() is that it performs better than -match, though the latter offers much more flexibility.
I'm trying to find a string and add the string needed for a program.
I need the code to look to see if the action = run fast already exists and if so do nothing.
$Input = GetContent "${Env:ProgramFiles}\myprogram\file.conf"
$replace = #"
[MyAction_Log]
action = run fast
"#
$Input -replace ('action = run fast') -replace ('\[MyAction_Log\]',$replace) | set-content "${Env:ProgramFiles}\myprogram\file.conf"
I would check before wantonly replacing things you think exist. Also, never use $Input as a variable name; it's an automatic variable and won't do what you think it will (treat it as read-only).
$path = "$Env:ProgramFiles\prog\file.conf"
$file = Get-Content -Path $path
$replacementString = #'
[MyAction_Log]
action = run fast
'#
if ($file -notmatch 'action\s=\srun\sfast')
{
$file -replace '\[MyAction_Log\]', $replacementString |
Set-Content -Path $path
}
An alternative that's able to cope with the action key being located anywhere in the [MyAction_Log] section
$Inside = $False
$Modified = $False
$Path = "$( $env:ProgramFiles )\prog\file.conf"
$NewLines = Get-Content $Path |
ForEach-Object {
if( $_.Trim() -like "[*]" ) { $Inside = $False }
if( $_.Trim() -like "*[MyAction_Log]*" ) { $Inside = $True }
If( $Inside -and $_ -like "action = *" -and $_ -notlike "*run fast*" ) { $Modified = $True; "action = run fast" } else { $_ }
}
If( $Modified ) { $NewLines | Set-Content $Path }
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"
}