Replace fist character in filename - regex

I am reading file names and if the file does not start with an N I need to replace that letter with the letter N.
foreach ($item in Get-ChildItem $Path) {
Write-host "working on $item"
if ($item -match "^(N\d{3})") {
# This file will already process correctly
} elseif ($item -match "^(\d{3})") {
$FilePath = $Path + $item
Write-Host $FilePath
#Rename this file so it will process correctly
Rename-Item $FilePath N$item -Force
Write-Host "Renaming: "$item "to N$item"
} elseif ($item -match "^[a-zA-Z](\d{3})") {
#replace first character with "N"
# How do I replace the first letter with an "N"?
}
}

You're thinking too complicated. This should suffice:
Get-ChildItem $Path | Rename-Item -NewName { $_.Name -creplace '^[^N]','N' }
or this, if you don't want those files "renamed" that already start with an uppercase N:
Get-ChildItem $Path |
Where-Object { $_.Name -cmatch '^[^N]' } |
Rename-Item -NewName { $_.Name -creplace '^[^N]','N' }

Related

Compare size of multiple subdirectory before and after a break in a Powershell script

I'm a beginner with Powershell, also forgive my English which isn't the best.
I have a directory with several subdirectories like this
Directory
My goal is to target directory that are updated while the script is running. So I made this script
$path = "C:\Users\s611284\Desktop\archive"
#Check that the directories have the correct name and calculate their size
Get-ChildItem -Force $path -ErrorAction SilentlyContinue -Directory | foreach {
$Size = (Get-ChildItem $_.fullname -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum/ 1Kb
$FolderName = $_.BaseName -match '1B(\d{6})_LEAP 1A version aout2021_(\d{4})-(\d{2})-(\d{2})T(\d{2})h(\d{2})m(\d{2})s_S(\d{6})' -or $_.BaseName -match '1B(\d{6})_SML 10_LEAP 1A version aout2021_(\d{4})-(\d{2})-(\d{2})T(\d{2})h(\d{2})m(\d{2})s_S(\d{5})'
$Folder = $_.BaseName
if ($FolderName -eq "true") {
write-host(" name $Folder is correct, $Size Kb")
}
else {
write-host( "name $Folder is incorrect")
}
}
#Break
Start-Sleep -Seconds 30
write-host( "end of break")
#directory size calculation after the break
Get-ChildItem -Force $path -ErrorAction SilentlyContinue -Directory | foreach {
$Size1 = (Get-ChildItem $_.fullname -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum/ 1Kb
$FolderName1 = $_.BaseName -match '1B(\d{6})_LEAP 1A version aout2021_(\d{4})-(\d{2})-(\d{2})T(\d{2})h(\d{2})m(\d{2})s_S(\d{6})' -or $_.BaseName -match '1B(\d{6})_SML 10_LEAP 1A version aout2021_(\d{4})-(\d{2})-(\d{2})T(\d{2})h(\d{2})m(\d{2})s_S(\d{5})'
$Folder1 = $_.BaseName
if ($FolderName1 -eq "true") {
write-host("name $Folder1 is correct, $Size1 Kb")
}
else {
write-host( "name $Folder1 is incorrect")
}
}
All of this is working great
So now I want to compare the size of the subdirectories before and after the break, to know which have been updated
I tried
if ( $FolderSize -eq $FolderSize1 )
{
Write-Output $True
}
Else
{
Write-Output $False
}
at the end of my second block but it isn't working..
I also tried Compare-object but I don't think this command will help in my case
I hope you guys will understand my post and help me
Thanks !
If I understood correctly, you're looking to filter those folders where it's Size has changed after 30 seconds, if that's the case, you could use a function so that you don't need to repeat your code. You can make your function return a hash table where the Keys are the folder's absolute path and the Values are their calculated size, once you have both results (before 30 seconds and after 30 seconds) you can run a comparison against both hash tables outputting a new object with the folder's Absolute Path, Size Before and Size After only for those folders where their calculated size has changed.
function GetFolderSize {
[cmdletbinding()]
param($path)
$map = #{}
Get-ChildItem $path -Directory -Force | ForEach-Object {
$Size = (Get-ChildItem $_.Fullname -Recurse | Measure-Object -Property Length -Sum).Sum / 1Kb
$FolderName = $_.BaseName -match '1B(\d{6})_LEAP 1A version aout2021_(\d{4})-(\d{2})-(\d{2})T(\d{2})h(\d{2})m(\d{2})s_S(\d{6})' -or $_.BaseName -match '1B(\d{6})_SML 10_LEAP 1A version aout2021_(\d{4})-(\d{2})-(\d{2})T(\d{2})h(\d{2})m(\d{2})s_S(\d{5})'
if ($FolderName) {
$map[$_.FullName] = $size
}
}
if($map) { $map }
}
$path = "C:\Users\s611284\Desktop\archive"
$before = GetFolderSize $path -ErrorAction SilentlyContinue
Start-Sleep -Seconds 30
$after = GetFolderSize $path -ErrorAction SilentlyContinue
foreach($key in $after.PSBase.Keys) {
if($before[$key] -ne $after[$key]) {
# this is a folder with a different size than before
[PSCustomObject]#{
FullName = $key
SizeBefore = $before[$key]
SizeAfter = $after[$key]
}
}
}
Not to take away from Santiago's helpful answer, but to provide an alternate solution, here's my take:
$path = "C:\Users\s611284\Desktop\archive"
$count = 0
$hashMap = #{}
While ($count -lt 2) {
Get-ChildItem -Path $path -Directory |
ForEach-Object -Begin {
$count++
$toMatch = "1B(\d{6})_LEAP 1A version aout2021_(\d{4})-(\d{2})-(\d{2})T(\d{2})h(\d{2})m(\d{2})s_S(\d{6})|1B(\d{6})_SML 10_LEAP 1A version aout2021_(\d{4})-(\d{2})-(\d{2})T(\d{2})h(\d{2})m(\d{2})s_S(\d{5})"
} -Process {
$folderMatch = $_.BaseName -match $toMatch
if ($folderMatch) {
$size = (Get-ChildItem -Path $_.FullName -Recurse -EA 0 | Measure-Object -Property "Length" -Sum).Sum / 1kb
if (-not$hashMap.ContainsKey($_.BaseName)) {
$hashMap.Add($_.BaseName,$size)
}
if ($count -ge 2) {
if ($hashMap[$_.BaseName] -ne $size) {
$_.BaseName + " " + "is a different size"
}
}
}
} -End {
if ($count -ne 2) {
Start-Sleep -Seconds 30
}
}
}
Personally, I hate the re-use of code and feel like something can always be done about it if you find yourself repeating code (copy/paste).
My question to you is:
Aren't those folder names pretty unique?
Could you not substitute it for a Wild Card Expression?
i.e.: $_.BaseName -like '*1A version aout2021*'
All in the name of "cleanliness" code. lol

Powershell - Searching for strings (in list) in a word document

I found some code for searching for strings in a Word Document. I altered it to suit my needs (I need to search from a very long list of strings). Unfortunately, I am getting a weird error.
While the script is running, it opens the word document, searches the word document and here is where it gets weird, instead of closing the document and opening the next, it presents me with a 'save as' dialog box and the script hangs until I cancel out of it. When I cancel out of it, my script continues.
Here is the script I'm using, would anyone see where I'm going south?
$results = #{}
Write-Host "Loading getStringMatch into memory" -ForegroundColor DarkMagenta
Function getStringMatch
{
# Loop through all *.doc files in the $path directory
Foreach ($file In $files)
{
Write-Host "Searching In ... $($File.FullName) " -ForegroundColor DarkYellow
$document = $application.documents.open($file.FullName,$false,$true)
$range = $document.content
If($range.Text -match ".{$($charactersAround)}$($findtext).{$($charactersAround)}"){
$properties = #{
File = $file.FullName
Match = $findtext
TextAround = $Matches[0]
}
$results += #(New-Object -TypeName PsCustomObject -Property $properties)
}
$document.close()
Write-Host "Closing Document ... $($File.FullName) " -ForegroundColor Red
}
#If($results){
# $results | Export-Csv $output -NoTypeInformation
#}
$application.quit()
}
$searchWords=Get-Content "C:\Temp\USDA_Search_For.txt"
Foreach ($sw in $searchWords)
{
Write-Host "Setting Variables ..." -ForegroundColor DarkMagenta
Set-StrictMode -Version latest
$path = "C:\Temp"
$files = Get-Childitem $path -Include *.docx,*.doc -Recurse | Where-Object { !($_.psiscontainer) }
$output = "C:\Temp\Found.csv"
$application = New-Object -comobject word.application
$application.visible = $False
$findtext = "First"
$charactersAround = 30
#$results = #{}
$findtext = $sw
Write-Host "Searching For ... $findtext" -ForegroundColor Green
getStringMatch
#clean up stuff
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($application) | Out-Null
Remove-Variable -Name application
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}
If($results){
$results | Export-Csv $output -NoTypeInformation
}
import-csv $output

Regular expression in power shell to convert character between double quotes to upper case

I want to write a powershell script which will convert a string which is present between double quotes in a file, and convert it into upper case.
The files are placed in different folders.
I am able to extract the string between the double quotes and convert it to upper case, but not able to replace it in the correct position.
Ex : This is the input string.
"e" //&&'i&&
The output should be
"E" //&&'i&&
This is what i have tried. Also this even i not replacing the content of the file.
$items = Get-ChildItem * -recurse
# enumerate the items array
foreach ($item in $items)
{
# if the item is a directory, then process it.
if ($item.Attributes -ne "Directory")
{
(Get-Content $item.FullName ) |
Foreach-Object {
if (($_ -match '\"'))
{
$str = $_
$ext = [regex]::Matches($str, '".*?"').Value -replace '"'
$ext = $ext.ToUpper()
Write-Host $ext
$_ = $ext
}
else { }
} |
Set-Content $item.FullName
}
}
This can do it. Really I wasn't following your code so I stripped it and modified the regex.
$items = Get-ChildItem "C:\Users\UsernameHere\Desktop\Folder123\*.txt"
# enumerate the items array
foreach ($item in $items){
# if the item is a directory, then process it.
if ($item.Attributes -ne "Directory"){
$content = (gc $item.FullName )
$content = $content.replace('"\w.*"',$matches[0].ToUpper)
$content | sc $item
}
}
If you had powershell 6 or 7:
'"hi"' -replace '".*"', { $_.value.toupper() }
"HI"
'"e" //&&''i&&' -replace '".*"', { $_.value.toupper() }
"E" //&&'i&&
I am able to print the upper case characters with the below code, but the file is not getting updated. It still has the old characters, How to update the fie with new contents.
$items = Get-ChildItem *.txt -recurse
# enumerate the items array
foreach ($item in $items)
{
# if the item is a directory, then process it.
if ($item.Attributes -ne "Directory")
{
(Get-Content $item.FullName ) |
Foreach-Object {
$str = $_
$_ = [regex]::Replace($_, '"[^"]*"', { param($m) $m.Value.ToUpper() })
Write-Host $_
} |
Set-Content $item.FullName
}
}

Renaming files Powershell, Cannot rename file does not exist

I try to remove " - 13234" from a bunch of files recursively. And if the file is already present after removing that part it should add (1), (2) and so on after the new file name.
I tested it on a small set of files and that was working fine. Now I tried it on a larger test set and it gave me some errors.
Like: Cannot rename file does not exist
Like: Cannot create a file when file already exist
For a lot of files it looks like it is doing fine actually.
$files = Get-ChildItem <location> -recurse -file | sort name | group-object -property {$_.fullname -replace "(.*)-.*?(\..*?)$",'$1$2'}
$files | foreach {
$inc = 0
if ($_.count -gt 1) {
rename-item -literalpath $_.group[0].fullname -NewName (($_.group[0].basename -replace "(.*)-.*?$",'$1') + $_.group[0].extension)
$inc++
for ($i = $inc; $i -lt $_.count; $i++) {
rename-item -literalpath $_.group[$i].fullname -NewName (($_.group[$i].basename -replace "(.*)-.*?$",'$1') + "($i)" + $_.group[$i].extension)
}
}
else {
rename-item -literalpath $_.group.fullname -NewName (($_.group.basename -replace "(.*)-.*?$",'$1') + $_.group.extension)
}
}
If the following files are in the same directory, the script probably will not work.
1 - 13234.txt
1.txt
The script first tries to change "1 - 13234.txt" to "1. txt".
You had better first check the existence of the file.
Get-ChildItem -File -Recurse |
where BaseName -match "(.*)-" |
Group-Object { $_.DirectoryName + "\" + $Matches[1].TrimEnd() + $_.Extension } |
foreach {
$fi = [IO.FileInfo]::new($_.Name)
$i = 0
$_.Group | Rename-Item -NewName {
do {
$newName = $fi.DirectoryName + "\" + $fi.BaseName + $(if($i){"($i)"}) + $fi.Extension
$script:i++
} while (Test-Path $newName)
$newName
} -PassThru
}
I would do something like this:
$Path = '<PATH TO THE ROOTFOLDER WHERE THE FILES TO RENAME ARE FOUND>'
Get-ChildItem -Path $Path -Filter '*-*' -File -Recurse | ForEach-Object {
# obtain the new file basename
$newBaseName = ($_.BaseName -split '-')[0].Trim()
$extension = $_.Extension # this includes the dot
$folder = $_.DirectoryName
# get an array of all filenames (name only) of the files with a similar name already present in the folder
$allFiles = #(Get-ChildItem $folder -Filter "$newBaseName*$extension" -File | Select-Object -ExpandProperty Name)
# for PowerShell version < 3.0 use this
# $allFiles = #(Get-ChildItem $folder-Filter "$newBaseName*$extension" | Where-Object { !($_.PSIsContainer) } | Select-Object -ExpandProperty Name)
# construct the new filename
$newFileName = $newBaseName + $extension
if ($allFiles.Count) {
$count = 1
while ($allFiles -contains $newFileName) {
$newFileName = "{0}({1}){2}" -f $newBaseName, $count++, $extension
}
}
Write-Host "Renaming '$($_.FullName)' to '$newFileName'"
$_ | Rename-Item -NewName $newFileName -Force
}
Hope that helps

Powershell search matching string in word document

I have a simple requirement. I need to search a string in Word document and as result I need to get matching line / some words around in document.
So far, I could successfully search a string in folder containing Word documents but it returns True / False based on whether it could find search string or not.
#ERROR REPORTING ALL
Set-StrictMode -Version latest
$path = "c:\MORLAB"
$files = Get-Childitem $path -Include *.docx,*.doc -Recurse | Where-Object { !($_.psiscontainer) }
$output = "c:\wordfiletry.txt"
$application = New-Object -comobject word.application
$application.visible = $False
$findtext = "CRHPCD01"
Function getStringMatch
{
# Loop through all *.doc files in the $path directory
Foreach ($file In $files)
{
$document = $application.documents.open($file.FullName,$false,$true)
$range = $document.content
$wordFound = $range.find.execute($findText)
if($wordFound)
{
"$file.fullname has $wordfound" | Out-File $output -Append
}
}
$document.close()
$application.quit()
}
getStringMatch
#ERROR REPORTING ALL
Set-StrictMode -Version latest
$path = "c:\Temp"
$files = Get-Childitem $path -Include *.docx,*.doc -Recurse | Where-Object { !($_.psiscontainer) }
$output = "c:\temp\wordfiletry.csv"
$application = New-Object -comobject word.application
$application.visible = $False
$findtext = "First"
$charactersAround = 30
$results = #{}
Function getStringMatch
{
# Loop through all *.doc files in the $path directory
Foreach ($file In $files)
{
$document = $application.documents.open($file.FullName,$false,$true)
$range = $document.content
If($range.Text -match ".{$($charactersAround)}$($findtext).{$($charactersAround)}"){
$properties = #{
File = $file.FullName
Match = $findtext
TextAround = $Matches[0]
}
$results += New-Object -TypeName PsCustomObject -Property $properties
}
}
If($results){
$results | Export-Csv $output -NoTypeInformation
}
$document.close()
$application.quit()
}
getStringMatch
import-csv $output
There are a couple of ways to get what you want. A simple approach is since you have the text of the document already lets perform a regex match on it and return the results and more. This helps in trying to address getting some words around in document.
We have the variable $charactersAround which sets the number of characters to match around the $findtext. Also I though the output was a better fit for a CSV file so I used $results to capture a hashtable of properties that, in the end, are output to a csv file.
Be sure to change the variables for your own testing. Now that we are using regex to locate the matches this opens up a world of possibilities.
Sample Output
Match TextAround File
----- ---------- ----
First dley Air Services Limited dba First Air meets or exceeds all term C:\Temp\20120315132117214.docx
Thanks! You provided a great solution to use PowerShell regex expressions to look for information in a Word document. I needed to modify it to meet my needs. Maybe, it will help someone else. It reads each line of the word document, and then uses the regex expression to determine if the line is a match. The output could easily be modified or dumped to a log file.
Set-StrictMode -Version latest
$path = "c:\Temp\pii"
$files = Get-Childitem $path -Include *.docx,*.doc -Recurse | Where-Object { !($_.psiscontainer) }
$application = New-Object -comobject word.application
$application.visible = $False
$findtext = "[0-9]" #regex
Function getStringMatch
{
# Loop through all *.doc files in the $path directory
Foreach ($file In $files) {
$document = $application.documents.open($file.FullName,$false,$true)
$arrContents = $document.content.text.split()
$varCounter = 0
ForEach ($line in $arrContents) {
$varCounter++
If($line -match $findtext) {
"File: $file Found: $line Line: $varCounter"
}
}
$document.close()
}
$application.quit()
}
getStringMatch
Good answer from #Matt.
I improved it a little (new PowerShell version have problems with the given array. And to search big amount of documents it runs out of memory.
Here is my improved version:
#ERROR REPORTING ALL
Set-StrictMode -Version latest
$path = "c:\Temp"
$files = Get-Childitem $path -Include *.docx,*.doc -Recurse | Where-Object { !($_.psiscontainer) }
$output = "c:\temp\wordfiletry.csv"
$application = New-Object -comobject word.application
$application.visible = $False
$findtext = "First"
$charactersAround = 30
$results = #{}
Function getStringMatch
{
# Loop through all *.doc files in the $path directory
Foreach ($file In $files)
{
$document = $application.documents.open($file.FullName,$false,$true)
$range = $document.content
If($range.Text -match ".{$($charactersAround)}$($findtext).{$($charactersAround)}"){
$properties = #{
File = $file.FullName
Match = $findtext
TextAround = $Matches[0]
}
$results += #(New-Object -TypeName PsCustomObject -Property $properties)
}
$document.close()
}
If($results){
$results | Export-Csv $output -NoTypeInformation
}
$application.quit()
}
getStringMatch
import-csv $output
Use the function like this:
PS> WordGrep -File ./Myfile.docx -Grep one, two, three
function WordGrep{
param(
[string]$File,
[string[]]$Grep,
[switch]$WordMode,
[switch]$EscapeMode
)
$WordApp = New-Object -comobject word.application
$WordApp.visible = $False
try {
$document = $WordApp.documents.open($File, $false, $true)
$arrContents = $document.content.text.split()
$found = $false
foreach ($line in $arrContents) {
foreach ($pattern in $Grep) {
if ($EscapeMode) {
$pattern = [Regex]::Escape($pattern)
}
if ($WordMode) {
$pattern = "\b${pattern}\b"
}
if ($line -imatch $pattern) {
write-host -ForegroundColor Cyan -NoNewLine "$file`:"
write-host " $line"
break;
}
}
}
$document.close()
}
finally {
$WordApp.quit()
}
}