Add filename increment in my script - powershell

I have created a script that will rename pst files to acl owner. It works. The only problem i have is when there are two pst files in the same folder.It gives them the same name. How can i add an increment in my script. It ried it with Si = 1 and Si++ but with no results.
Here is my script:
Get-ChildItem C:\Users\tester\* -Filter *.pst -recurse |
ForEach-Object{
$owner = ( $_ | Get-Acl ).Owner.Split("\")[1]
$newname = "$owner.pst"
$_ | Rename-Item -NewName $newname -Verbose -WhatIf
}

This is a quick and fast work around by adding names already used to an array and checking each time you loop.
$increment = 1
$alreadyProcessed = #()
Get-ChildItem C:\Users\tester\* -Filter *.pst -recurse |
ForEach-Object{
$owner = ( $_ | Get-Acl ).Owner.Split("\")[1]
$newname = "$owner.pst"
if($alreadyProcessed.Contains($newName))
{
$newName = "$owner`$increment.pst"
$increment++
}
$alreadyProcessed += $newname
$_ | Rename-Item -NewName $newname -Verbose -WhatIf
}
This will ensure a unique name each time.

If you plan to run your script multiple times in the same place then you should check to see what names are available otherwise your counter will reset to 1 but those files would already exist.
function Get-NextName ($file) {
$existing = (Get-Item "$file.*").Name
$i = 1
while ($existing -contains "$file.$i") {
$i++
}
"$file.$i"
}
And then adjust your function:
Get-ChildItem C:\Users\tester\* -Filter *.pst -recurse |
ForEach-Object{
$owner = ( $_ | Get-Acl ).Owner.Split("\")[1]
$newname = Get-NextName "$owner.pst"
$_ | Rename-Item -NewName $newname -Verbose -WhatIf
}

Related

Test-Path cmdlet fails only for one file out of 20

I want to get the duplicates from a folder structure and copy all of them to a single folder, while renaming (so they don't overwrite). I would like the first file from a duplicates group to be copied with it's original name, and for the rest to add "_X" at the end of the name.
I wrote a code that almost works, but at some point it just overwrites the first file copied. Only one file is being overwritten, the rest are renamed and copied like intended.
Get-ChildItem $SourcePath -Recurse -File -Force | Group-Object -Property Name | Where-Object {$_.Count -gt 1} | Select-Object -ExpandProperty Group |
ForEach-Object {
$SourceFile = $_.FullName
$FileName = $($_.BaseName + $_.Extension)
$DestFileName = Join-Path -Path $DestinationPath -ChildPath $FileName
if (Test-Path -Path $DestFileName) {
$DestinationFile = "$DestinationPath\" + $_.BaseName + "_" + $i + $_.Extension
$i+=1
} else {
$DestinationFile = $DestFileName
}
Copy-Item -Path $SourceFile -Destination $DestinationFile
}
I don't see the actual problem but you could rewrite the code without using Test-Path. Remove Select-Object -ExpandProperty Group too, then iterate over each group's elements. Increment a counter and append it to all file names except the first one.
Get-ChildItem $SourcePath -Recurse -File -Force | Group-Object -Property Name | Where-Object Count -gt 1 |
ForEach-Object {
$i = 0
foreach( $dupe in $_.Group ) {
$SourceFile = $dupe.FullName
$DestinationFile = Join-Path -Path $DestinationPath -ChildPath $dupe.BaseName
if( $i -gt 0 ) { $DestinationFile += "_$i" }
$DestinationFile += $dupe.Extension
Copy-Item -Path $SourceFile -Destination $DestinationFile
$i++
}
}

Move-Item doesn't move file

I've tried to rename a csv with powershell and then move it automatically to another folder, when there's no file.
Originally, the csv-name looks like this: import_9999_2020-08-13_132238.csv but the part with 9999 can also include just 2 or 3 digits.
My actual Code looks like:
#Import of path and target-path
$path = "\\network-path\subfolder\subfolder\subfolder\subfolder\subfolder1\"
$target_path = "\\network-path\subfolder\subfolder\subfolder\subfolder\subfolder2\"
#endless loop
$a=$true
while($a -eq $true){
$Files = gci $path
$TargetFiles = gci $target_path
#wait 5 minutes if path is empty
if(($Files).Count -eq 0){
sleep -Seconds 300
}
#if path is filled with one or more files
else {
#if file in target-path is processed (from another program)
if(($TargetFiles).count -eq 0){
#rename and move the latest file
get-childitem -path $path -Filter "import_*.csv"|
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1 |
Rename-Item -NewName {($_.Name.Substring(0,($_.Name.Length)-22))+".csv"} |
Move-Item -Destination $target_path +"$($_.Name).csv"
}
sleep -Seconds 20
}
}
It works partly and renames the csv, but it doesn't move it to the target-path. The path is correct, i've copied it from the Windows-Explorer.
Any Ideas, why the program doesn't work completely? Thanks
For better readability, I would split the code where you find the original file, rename it and move it to the target path into several lines.
Also, Move-Item can rename the file aswell, so no need to do a Rename-Item first:
$file = Get-ChildItem -Path $path -Filter "import_*.csv" -File |
Sort-Object -Property $_.LastWriteTime |
Select-Object -Last 1
# create the new name for the file.
# or use regex: $newName = '{0}{1}' -f ($file.BaseName -replace '(_\d{4}-\d{2}-\d{2}_\d+)$'), $file.Extension
$newName = '{0}{1}' -f (($file.BaseName -split '_')[0..1] -join '_'), $file.Extension
# now move the file and rename at the same time
$file | Move-Item -Destination (Join-Path -path $target_path -ChildPath $newName)
I changed CreationTime to LastWriteTime to really get the latest file.
I also added the -File switch to the Get-ChildItem cmdlet, because only for old PowerShell versions you need to use where-object { -not $_.PSIsContainer }
If you want to pass it down the pipeline add -Passthru to the Rename-Item cmdlet
Somefile.txt | Rename-Item -NewName {$_.basename + "abc" + $_.ext} | # nothing in the pipeline
Somefile.txt | Rename-Item -NewName {$_.basename + "abc" + $_.ext} -Passthru | # now the new named fileinfo object is in the pipeline, contained in automatic variable $_

How do I change extension file in Powershell?

I want to change extension of some of my file.
I tried this code, but it still return error
The input to the script block for parameter 'NewName' failed.
Anyone can help please?
$b = "TA"
$c = "70"
$Path_1 = "C:\Users\hh\Documents"
$Found = Get-ChildItem -Name "$Path_1\*$b-$c*.txt" | Rename-Item -NewName { $_.Name.Replace('.txt','.csv') }
This should do it:
$b = "TA"
$c = "70"
$Path_1 = "C:\Users\hh\Documents"
$Found = Get-ChildItem -Filter ($Path_1 + "\*" + $b + "-" + $c + "*.txt")
foreach ($entry in $Found){
Rename-Item -Path $entry.FullName -NewName ($entry.Name.Replace('.txt','.csv'))
}
In this case, it is much easier to use both the -Path and -Filter parameters on the Get-ChildItem cmdlet.
If you want to make sure you do not by accident change folder names, in PowerShell version 3.0 and up, add the -File switch aswell like I'm doing here:
$b = "TA"
$c = "70"
$Path_1 = "C:\Users\hh\Documents"
Get-ChildItem -Path $Path_1 -Filter "*$b-$c*.txt" | Rename-Item -NewName { '{0}.csv' -f $_.BaseName } -WhatIf
# For PowerShell versions below 3.0, you need to add an extra Where-Object clause:
# Get-ChildItem -Path $Path_1 -Filter "*$b-$c*.txt" | Where-Object { !$_.PSIsContainer } | Rename-Item -NewName { '{0}.csv' -f $_.BaseName } -WhatIf
Of course, you can also use .NET [System.IO.Path]
Get-ChildItem -Path $Path_1 -Filter "*$b-$c*.txt" | Rename-Item -NewName { [System.IO.Path]::ChangeExtension($_.Name, ".csv") } -WhatIf
Remove the -WhatIf switch if you are satisfied with the results.
The rest of the error message is "You cannot call a method on a null-valued expression". "get-childitem -name" only outputs a string, not an object with properties. Take out the -name after get-childitem, and it would work.

Deleting a created file at every iteration

$getFiles = Get-ChildItem -Path $readDir -File -Include "*.doc","*.docx","*.xlsx"-Recurse | %{
if(($_ -match "\.doc$") -or ($_ -match "\.docx$")){
$Doc = $word.Documents.Open($_.fullname)
$nameDoc = $fileSaveLoc + $_.Name.Replace(".docx",".txt").replace(".doc",".txt")
$Doc.saveas([ref] $nameDoc, [ref] 5)
$Doc.close()
if((Get-ChildItem "I:\temp\").length -ne 0){
$locations = (Get-Item "I:\temp\"), (Get-ChildItem "I:\temp\" -Directory -recurse) | % {
Get-ChildItem -File $_.FullName | Select-String -List -Pattern '^\d{3}-?\d{2}-?\d{4}$' |
% Path
}
if($locations -ne $null){
$locations | out-file "I:\temp\SSN_FILES.txt"
#Get-ChildItem "I:\temp\" -exclude "fullpath.txt","SSN_FILES.txt" | remove-item
}else{
Get-ChildItem "I:\temp\" -exclude "fullpath.txt","SSN_FILES.txt" | remove-item
}
}
}
elseif($_ -match "\.xlsx$"){
$workbook = $excel.Workbooks.Open($_.FullName)
$csvFilePath = "I:\temp\" + $_.Name.Replace(".xlsx",".csv")
#$csvFilePath = $_.FullName -replace "\.xlsx$", ".csv"
$workbook.SaveAs($csvFilePath, [Microsoft.Office.Interop.Excel.XlFileFormat]::xlCSV)
$workbook.Close()
if((Get-ChildItem "I:\temp\").length -ne 0){
$locations = (Get-Item "I:\temp\"), (Get-ChildItem "I:\temp\" -Directory -recurse) | % {
Get-ChildItem -File $_.FullName | Select-String -List -Pattern '^\d{3}-?\d{2}-?\d{4}$' |
% Path
}
if($locations -ne $null){
$locations | out-file "I:\temp\SSN_FILES.txt"
#Get-ChildItem "I:\temp\" -exclude "fullpath.txt","SSN_FILES.txt" | remove-item
}else{
Get-ChildItem "I:\temp\" -exclude "fullpath.txt","SSN_FILES.txt" | remove-item
}
}
}
}
So this basically says:
check for a file matching doc/docx/xlsx
convert them into a file that can be parsed through
parse through each file at every iteration and compare it to a regex
if the regex is not null, then output it to a file with the file path
otherwise, delete it and anything else that was created except for two files
restart the process at the next file
Now the problem I am encountering is that the files aren't being deleted. I can't get them to be removed when they are created, when I know they don't match the regex. Setting ($locations -eq $true) doesn't solve that issue because it never goes into the first conditional statement.
The folder should contain only the two files that were created and possibly the ones that match the regex.

Powershell renaming files recursively, not renaming duplicates

I have some powershell code that is meant to take specific characters out of file names recursively. This works great, unless taking the characters out causes 2 files to match names in the same folder.
I found the Powershell here PowerShell script to remove characters from files and folders but it doesn't solve the issue I am having.
$characters = "$#"
$regex = "[$([regex]::Escape($characters))]"
$filesandfolders = Get-ChildItem -recurse "D:\projects\filenameCleaner\TEST" | Where-Object {$_.name -match $regex}
$filesandfolders | Where-Object {!$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
Rename-Item -path $_.Fullname -newname $New -passthru
}
$filesandfolders | Where-Object {$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
Rename-Item -path $_.Fullname -newname $New -passthru
}
I'm not very versed in Powershell yet and would love some help with this. I just want the script that, if it finds a duplicate, it should just add a (1) or (2) etc at the end, depending on how many there are.
Please don't just give me an answer, explain it so that I can learn what is happening
I think the way to tackle this is to create a new PSOBJECT which contains your source and target paths. You can then use the Group-object to group on the target path which will give you a count which you can then use to determine which files will need a number suffix.
Something like this:
Get-ChildItem -recurse "D:\projects\filenameCleaner\TEST" | ? {!$_.PsIscontainer} | % {
New-Object psobject -Property #{
source = $_.FullName
target = ($_.FullName -replace [regex]::Escape($_.Name)) + ($_.Name -replace '[\$#]+', '')
}
} | Group-Object target | % {
$g = $_.Group
0..($g.Count - 1) | % {
New-Object psobject -Property #{
source = $g[$_].source
target = $g[$_].target -replace '\.', #('.', "($($_)).")[$_ -gt 0]
}
}
} | % {Rename-Item -Path $_.source -NewName $_.Target}
The nice thing about doing it this way is that it can two, three or more duplcates
I have only done the code for files so you would need another version for folders.
I was able to do this by adding an if-statement that checks the new path
$characters = "$#}"
$regex = "[$([regex]::Escape($characters))]"
$path = "D:\projects\filenameCleaner\TEST"
$filesandfolders = Get-ChildItem -recurse $path | Where-Object {$_.name -match $regex}
$filesandfolders | Where-Object {!$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
$newPath = $path+"\"+$New
$loop = 0
while (Test-Path $newPath) {
$loop = $loop + 1
$basename = $_.basename -Replace $regex
$New = $basename + " ("+$loop+")"+$_.extension
$newPath = $path+"\"+$New
}
"Rename `""+$_.name+"`" to `""+$New+"`""
Rename-Item -path $_.Fullname -newname $New -passthru
}
$filesandfolders | Where-Object {$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
$newPath = $path+"\"+$New
$loop = 0
while (Test-Path $newPath) {
$loop = $loop + 1
$basename = $_.basename -Replace $regex
$New = $basename + " ("+$loop+")"+$_.extension
$newPath = $path+"\"+$New
}
"Rename `""+$_.name+"`" to `""+$New+"`""
Rename-Item -path $_.Fullname -newname $New -passthru
}
-- Another update that handles the extensions separately when adding the number to the string for duplicates. I was having issues that the number would go onto the file name after the extension which is obviously a no-go