Replace text between quotes in XML - powershell

I'm trying to find a way to change unknown text (could be anything) in an XML file for a printer migration.
The text that is in question is
PrintProcessor="hpcpp111"
The Print Processor section could contain anything as this varies depending on printer model, type and driver. I would prefer to use powershell if possible as I am trying to understand scripting and how it all works, but I find it a little confusing. I may need to manually edit thousands of these files as we are migrating 5,500 printers.
I have found some code that gets it close for example
function Reset-InfoPathTemplateLink {
Param(
[string]$FilePath,
[string]$FileExtension,
[string]$OldPath,
[string]$NewPath
)
$files = Get-ChildItem $FilePath -Filter $FileExtension
foreach ($file in $files) {
(Get-Content $file.fullname) |
ForEach-Object {$_ -replace $OldPath,$NewPath} |
Set-Content $file.fullname
} #end foreach
} #end function
and if I dot-source it and then run this command
Reset-InfoPathTemplateLink -FilePath "c:\test2" -FileExtension ".xml" -OldPath "PrintProcessor=""" -NewPath "PrintProcessor='"WinPrint"'"
The WinPrint gets added to the beginning of the current print processor, which isnt ideal. If anyone has tips on how to get this to work, or if they can suggest a better method of doing this I would appreciate it

this is how you can use PowerShell to replace a pattern within a text file:
$path="c:\myPath.txt"
(Get-Content $path) -replace '(PrintProcessor=")(.*)(")','$1WinPrint$3' | Set-Content $path

Related

In Powershell how do I find file names that contains text or files that have the text in them

I'm searching through directories recursively. I want to find files that contain text i'm looking for OR the text is in the content of the file.
For example, if I search for "hello", i'd normally do this:
Find matching file names:
get-childitem -filter "*hello*"
Find files that have text in them:
get-childitem -recurse | select-string -pattern "*hello*"
But I want to do both at the same time. Which means you could have files that don't have "hello" in the name but it does appear in the contents of the file. Or visa versa.
EDIT: I considered using where-object with an -or but having trouble figuring out how to construct that properly.
EDIT: My mistake, meant to include select-string in the example.
Ideas?
Thanks
I don't think its possible to use -Filter because you might be excluding those files which's content could contain the word you're looking for.
An easy approach I could think of, is looping through all files in $startPath recursively and, if the word is contained in the file's name, go to the next iteration with continue or break if you want to stop the loop at first finding, and of course, use -Raw for Get-Content:
$startPath = 'C:\path\to\startFolder'
$magicWord = 'hello'
foreach($file in Get-ChildItem $startPath -Recurse -File)
{
if($file.Name -match $magicWord)
{
$file
continue
# or break here if you want to stop the loop
}
if((Get-Content $file.FullName -Raw) -match $magicWord)
{
$file
# break here if you want to stop the loop
}
}
Not sure if using this would be faster or not:
if([system.io.file]::ReadAllText($file.FullName) -match $magicWord)
{
...
}

Sensitive word search with powershell

I am somewhat new to PowerShell so any help I would appreciate.
I am trying to put a PS script together so that I can search a file for sensitive words before transferring it from one network to another. Like 'Classified' and multiple other words that I can add to a word bank in a text file vice updating the code every time
Right now I am forced to use PS 2 windows 7 and server 2008
Select-String -Path e:\transfer_folder\*.* -pattern Classified,restricted
Then I can get an output for any hits on the list of words so that I can find them. I am trying to speed up my searching through hundreds of pages of documents with what I like to call a dirty word search so I do not put something that should not go on the wrong network.
You've got the right idea. The -Pattern tack in powershell can usually be called on to work with regular expressions. If you've never worked with regular expressions, take a look at this beginner's guide to using regex pattern matching. What you probably want is a set of variables that you can use to dynamically pick out those sensitive keywords.
The short and simple answer is that you want to use a pipe to separate your options for pattern, and pass it in as a string.
Select-String -Path e:\transfer_folder\*.* -pattern "Classified|Restricted"
Also, you might want to think about doing this at the file level rather than just importing all of your stuff in willynilly like that. I would go for something like:
$files = #(Get-ChildItem -Path E:\transfer_folder\ -Filter "*.txt|*.etc").FullName
(The # symbol means that you get your output as an array. The .FullName means that you're only selecting the FullName field from the object that's being produced by the command.)
Then you can process each file individually, like:
Foreach ($file in $files) {
Write-Host "Processing $file"
echo (Select-String -Path $file -Pattern $pattern)
}
One of the reasons that I love powershell is how comparatively easy it is to perform these types of matching operations. If you dig into Regex, you'll notice that you can represent "OR" as "|". So you have two options to do this logically:
Just hard write it out
$pattern = "Classified|Forbidden|Death|Danger"
Do it dynamically
Scripting is all about not having to do things more than once, right? So you'll probably want to encapsulate this in a function or something. Or maybe you want to get your words from a text file? You can be like:
(might take some tweaking)
function Get-ForbiddenWords ([string[]]$words, [string]$folder) {
ForEach ($word in $words) {
$pattern += "$word|"
}
#remove trailing pipe
$pattern -replace ".$"
$files = #(Get-ChildItem -Path $folder -Filter "*.txt|*.etc").FullName
Foreach ($file in $files) {
Write-Host "Processing $file"
echo (Select-String -Path $file -Pattern $pattern)
}
}
Now you can put this in your powershell profile and invoke it with
Get-ForbiddenWords -words secret dangerous whatever -folder E:\transfer_folder\

Powershell Set-Content is duplicating lines in .ini file

Go easy on me, first time posting.
I'm trying to use Powershell to Get-Content from an INI file. Particularly, I need to change two separate lines in the file. It runs, but instead of just replacing those 2 lines, it duplicates everything. It also doesn't replace the line I'm trying to tell it to replace, but instead it just adds my new line leaving the original in as well.
$FilePath = "C:\Users\folder\*.ini"
(Get-Content $FilePath) |
ForEach-Object {
$_ -replace "MailBell=0","MailBell=1"
$_ -replace "MailWindow=0","MailWindow=1"
} |
Set-Content $FilePath
There is a look at the code. Any help is greatly appreciated.

Powershell - clarification about foreach

I am learning powershell and I need someone to give me an initial push to get me through the learning curve. I am familiar with programming and dos but not powershell.
What I would like to do is listing all files from my designated directory and pushing the filenames into an array. I am not very familiar with the syntax and when I tried to run my test I was asked about entering parameters.
Could someone please enlighten me and show me the correct way to get what I want?
This is what powershell asked me:
PS D:\ABC> Test.ps1
cmdlet ForEach-Object at command pipeline position 2
Supply values for the following parameters:
Process[0]:
This is my test:
[string]$filePath = "D:\ABC\*.*";
Get-ChildItem $filePath | foreach
{
$myFileList = $_.BaseName;
write-host $_.BaseName
}
Why was ps asking about Process[0]?
I would want to ps to list all the files from the directory and pipe the results to foreach where I put each file into $myFileList array and print out the filename as well.
Don't confuse foreach (the statement) with ForEach-Object (the cmdlet). Microsoft does a terrible job with this because there is an alias of foreach that points to ForEach-Object, so when you use foreach you have to know which version you're using based on how you're using it. Their documentation makes this worse by further conflating the two.
The one you're trying to use in your code is ForEach-Object, so you should use the full name of it to differentiate it. From there, the issue is that the { block starts on the next line.
{} is used in PowerShell for blocks of code related to statements (like while loops) but is also used to denote a [ScriptBlock] object.
When you use ForEach-Object it's expecting a scriptblock, which can be taken positionally, but it must be on the same line.
Conversely, since foreach is a statement, it can use its {} on the next line.
Your code with ForEach-Object:
Get-ChildItem $filePath | ForEach-Object {
$myFileList = $_.BaseName;
write-host $_.BaseName
}
Your code with foreach:
$files = Get-ChildItem $filePath
foreach ($file in $Files)
{
$myFileList = $file.BaseName;
write-host $file.BaseName
}

Powershell add text to files if does not exist

I would like to create a Powershell script that adds text to *.jsp *.sh *.cmd files. I would also like it to check if the text already exist in that file and if it does then it skips the file. So far I have found Select-String which will find the text in the file. How do I then use this information to add text to top of the files that do not have the text in them? I also found Add-Content which seems to add the content I want but I would like to do it at the beginning and have some logic to not just keep re-adding it every time I run the ps1.
Select-String -Pattern "TextThatIsInMyFile" -Path c:\Stuff\batch\*.txt
Add-Content -Path "c:\Stuff\batch\*.txt" -Value "`r`nThis is the last line"
Very similar to what #MikeWise has, but a little better optimized. I have it pulling the data and making the provider filter the files returned (much better than filtering afterwards). Then I pass it to a Where statement using Select-String's -quiet parameter to provide boolean $true/$false to the Where. That way only file that you want to look at are even looked at, and only those missing the text you need are altered.
Get-ChildItem "C:\Stuff\Batch\*" -Include *.jsp,*.sh,*.cmd -File |
Where{!(Select-String -SimpleMatch "TextThatIsInMyFile" -Path $_.fullname -Quiet)} |
ForEach{
$Path = $_.FullName
"TextThatIsInMyFile",(Get-Content $Path)|Set-Content $Path
}
Edit: As you discovered the \* does not work with -Recursive. Use the following if you need to recurse:
Get-ChildItem "C:\Stuff\Batch" -Include *.jsp,*.sh,*.cmd -File -Recurse
Refering to the manual: https://technet.microsoft.com/en-us/library/hh849903.aspx
Specifically:
Outputs
The output type is the type of the objects that the cmdlet emits.
• Microsoft.PowerShell.Commands.MatchInfo or System.Boolean
By default, the output is a set of MatchInfo objects, one for each match found.
If you use the Quiet parameter, the output is a Boolean value indicating whether the pattern was found.
I also think that you also need to do this per file. So (presumbably):
$files = Get-ChildItem "C:\Stuff\batch" -Filter *.txt
for ($i=0; $i -lt $files.Count; $i++)
{
$filename = $files[$i].FullName
$b = Select-String -Quiet -Pattern "TextThatIsInMyFile" -Path $fileName
if (-not $b)
{
Add-Content -Path $fileName -Value "`r`nTextThatIsInMyFile"
}
}
I tested this and I think it does what you want, i.e. adds the text to the end of files that do not have it, and not doing it more than once.