powershell Rename Script - Mostly functional, looking to finish and fully automate task - powershell

This is my first post to Stack Overflow, and I avoided asking for help and tried to figure this out as much as I could on my own. I have very little scripting experience, but I'm looking to learn. I chose this project as a place to start, and felt like my goals for this script got out of hand rather quickly.
What I have is a functional, probably very bloated, script, that only does a fraction of what I wanted it to do for me.
Here is the task I have, that before we were doing 100% by hand - manually file by file:
I have MP3 files of high importance that need to get copied off of an SD card and eventually moved to a shared drive that is backed up regularly.
These files are in the file name format of MMDDYYYYHHMMSS_RC-700R.mp3
We store these files on our data share in its own directory, organized further by the month, leading me try to add that in as part of my script, but it is a less important feature.
My goal was to safely move these files off of the SD card, rename them - removing the TIME of the file, but if there were 2 (or more) files made on the same date to append an alphabetical iterative count up. I tried to comment out every step, not only for others to read, but to keep myself apprised of what I was trying to accomplish in each section of the code.
#Start by Clearing Host and Saving Transcript
Clear-Host
$date = Get-Date -Format 'dddd MM-dd-yyyy'
Start-Transcript -Path "F:\Script$date.txt" -NoClobber -Append
#Set locations - SD Card:Import
$Import = "C:\Users\Death\Desktop\Minutes"
$Source = "F:\Minutes\"
$Destination = "F:\Final\"
#Test if $Source Exists, if not Create directory for Files to be transferred to
if(!(Test-Path -Path $Source)) {
New-Item -ItemType directory -Path $Source
Write-Host "Folder path has been created successfully at: " $Source
}
else {
Write-Host "The given folder path $Source already exists";
}
#Test if $Destination exists and create if false
if(!(Test-Path -Path $Destination)) {
New-Item -ItemType directory -Path $Destination
Write-Host "Folder path has been created successfully at: $Destination "}
else {
Write-Host "The given folder path $Destination already exists";
}
#Copy Items from $Import location
Get-ChildItem -Path $Import -Filter *.mp3 | Copy-Item -Destination $Source
#Rename Files adding Dashes between MM DD YYYY and HHMMSS
Get-ChildItem -Path $Source -Filter *.mp3 | Rename-Item -NewName {$_.Name -Replace ('^\n*(\d{2})(\d{2})(\d{4})(\d{6}).(?:...)(\d{3})\w','TC $1-$2-$3-$4')}
#Move files into $Destination\Year\Month created - Need to add
#Move files into $NewPath\Year\Month created
#Running in to problems with moving the files after they have been renamed - Likely due to
Get-ChildItem -File -Path $Source -Filter '*.mp3' |
ForEach-Object {
$Year = $_.LastWriteTime.Year
$Month = $_.LastWriteTime.Month
$Monthname = (Get-Culture).DateTimeFormat.GetMonthName($Month)
$ArchDir = "$Destination\$Year\$Monthname\"
if (-not (Test-Path -Path $ArchDir)) { New-Item -ItemType "directory" -Path $ArchDir | Out-Null }
Move-Item -Path $Source\*.mp3 -Destination $ArchDir -Verbose
}
#Would like to add a list of files renamed and where they moved to instead of just Enter to exit
Read-Host -Prompt "Press Enter to exit"
I have had little parts of my goals working in other versions of this code, but this is my most functional one.
Problems that currently exist:
I haven't figured out how to safely iterate files created on the same day when renaming files and leaving off the TIME - So as it is currently functioning I have it leaving the TIME on the file and I am manually removing it and adding any letters when needed
It is throwing all of the files in to $Destination in the Month of the first file, and putting all the files in there (not a huge issue as this was extra)
I would like it to list every move operation as well as any errors. It is doing this now with the $Source & $Destination folders & the -Verbose on the final GCI - Move operation.
Until I demonstrate that the script is 100% functional I have all locations in the script on my local machine to keep my boss happy.
I am sorry for the wall of text & Thanks in advance for any assistance.

This looks way over-complicated.
For the file parts, it could be as simple as this...
As per your stated use case:
My goal was to safely move these files off of the SD card,
rename them - removing the TIME of the file,
Clear-Host
$SourcePath = 'C:\Temp'
$TargetPath = 'C:\Temp\TempChild'
Get-ChildItem -Path $SourcePath -Filter '*.mp3' |
ForEach {
$FileName = $PSItem
Try
{
If ((Get-Item -Path "$TargetPath\$($FileName.Name)" -ErrorAction Stop))
{
Rename-Item -Path "$TargetPath\$($PSItem.Name)" -NewName "$($FileName.Name -replace '\d+_')" -WhatIf
Move-Item -Path $FileName.FullName -Destination $TargetPath -WhatIf
}
}
Catch {Move-Item -Path $FileName.FullName -Destination $TargetPath -WhatIf}
}
# Results
<#
# When the file does not exists
What if: Performing the operation "Move File" on target "Item: C:\Temp\04122021143000_RC-M1234R.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M1234R.mp3".
# When the file exists
What if: Performing the operation "Rename File" on target "Item: C:\Temp\TempChild\04122021143000_RC-M1234R.mp3 Destination: C:\Temp\TempChild\RC-M1234R.mp3".
What if: Performing the operation "Move File" on target "Item: C:\Temp\04122021143000_RC-M1234R.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M1234R.mp3".
#>
You can create new paths, without using New-Item. Just use the -Force switch/parameter on the copy/move action.
Test-Path -Path "$TargetPath\test1"
# Results
<#
False
#>
Get-ChildItem -Path $SourcePath -Filter '*.mp3' |
Copy-Item -Destination "$TargetPath\test1" -Force -WhatIf
# Results
<#
What if: Performing the operation "Copy File" on target "Item: C:\Temp\04122021143000_RC-M1234R.mp3 Destination: C:\Temp\TempChild\test1".
#>
Get-ChildItem -Path $SourcePath -Filter '*.mp3' |
Copy-Item -Destination "$TargetPath\test1" -Force
Test-Path -Path "$TargetPath\test1"
Get-ChildItem -Path $TargetPath -Filter '*.mp3'
# Results
<#
True
Directory: C:\Temp\TempChild
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/8/2021 6:04 PM 39 04122021143000_RC-M12345R.mp3
-a---- 4/8/2021 6:04 PM 39 04122021143000_RC-M1234R.mp3
#>
As for
but if there were 2 (or more) files made on the same date to append an alphabetical iterative count up.
I am not sure why you use alpha vs numeric. Since alpha would limit you to 26 letters before you'd end up having to double that string. Numeric of course, you can just look at the number and increment by 1.
Here's what I mean, just using your file name vs file properties. Yet, you can do the same approach, by looking at the file properties in the mix.
Clear-Host
$SourcePath = 'C:\Temp'
$TargetPath = 'C:\Temp\TempChild'
Get-ChildItem -Path $SourcePath -Filter '*.mp3' |
ForEach {
$FileName = $PSItem
Try
{
If (
-Not ((Get-ChildItem -Path $TargetPath -Filter $FileName.Name) -match "(?<=RC-M\d+R\d)") -and
($FileName.Name -replace '_\.*') -match
((Get-ChildItem -Path $TargetPath -Filter $FileName.Name) -replace '_\.*')
)
{
Rename-Item -Path "$TargetPath\$($FileName.Name)" -NewName "$(
$FileName.Name -replace 'R\.', 'R1.'
)" -ErrorAction Stop -WhatIf
Move-Item -Path $FileName.FullName -Destination $TargetPath -Verbose -WhatIf
}
}
Catch
{
$FileToIncrement = (
Get-ChildItem -Path $TargetPath |
Where-Object -Property Name -Match ($FileName.BaseName -replace 'R\d+')
).FullName
if($FileToIncrement -match "(?<=RC-M\d+R)(?<bv>\d+)")
{
Rename-Item -Path $FileToIncrement -NewName (
$FileToIncrement -replace "(?<=RC-M\d+R)(\d+)",
("{0:0000}" -f (([int]::Parse($matches.bv)+1)))
) -Verbose -WhatIf
Move-Item -Path $FileName.FullName -Destination $TargetPath -Verbose -WhatIf
}
}
}
# Results - when incrementer does not exist for the matched time string
<#
VERBOSE: Performing the operation "Rename File" on target "Item: C:\Temp\TempChild\04122021143000_RC-M12345R.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M12345R1.mp3".
VERBOSE: Performing the operation "Move File" on target "Item: C:\Temp\04122021143000_RC-M12345R.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M12345R.mp3".
VERBOSE: Performing the operation "Rename File" on target "Item: C:\Temp\TempChild\04122021143000_RC-M1234R.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M1234R1.mp3".
VERBOSE: Performing the operation "Move File" on target "Item: C:\Temp\04122021143000_RC-M1234R.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M1234R.mp3".
#>
# Results - when incrementer exists for the matched time string
<#
What if: Performing the operation "Rename File" on target "Item: C:\Temp\TempChild\04122021143000_RC-M12345R1.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M12345R0002.mp3".
What if: Performing the operation "Move File" on target "Item: C:\Temp\04122021143000_RC-M12345R.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M12345R.mp3".
What if: Performing the operation "Rename File" on target "Item: C:\Temp\TempChild\04122021143000_RC-M1234R1.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M1234R0002.mp3".
What if: Performing the operation "Move File" on target "Item: C:\Temp\04122021143000_RC-M1234R.mp3 Destination: C:\Temp\TempChild\04122021143000_RC-M1234R.mp3".
#>

Related

Need Help to Create a Folder based off a filename via Powershell

I have
\\192.168.1.2\Video\PodCasts\MyPodcast S01E01.mp4
\\192.168.1.2\Video\PodCasts\MyPodcast S01E02.mp4
\\192.168.1.2\Video\PodCasts\MyPodcast S01E02.mp4
\\192.168.1.2\Video\PodCasts\MyPodcast S02E01.mp4
\\192.168.1.2\Video\PodCasts\MyPodcast S02E02.mp4
\\192.168.1.2\Video\PodCasts\MyPodcast S02E03.mp4
\\192.168.1.2\Video\PodCasts\MyPodcast S03E01.mp4
\\192.168.1.2\Video\PodCasts\MyPodcast S03E02.mp4
\\192.168.1.2\Video\PodCasts\MyPodcast S03E03.mp4
I want to create
\\192.168.1.2\Video\PodCasts\Podcasts S01
\\192.168.1.2\Video\PodCasts\Podcasts S02
\\192.168.1.2\Video\PodCasts\Podcasts S03
And I have a PS Script to create the folders and move the files within it..
I have so far
$Show = Read-Host "Show"
cd "\\192.168.1.90\video\$Show"
Get-ChildItem -Filter "$Show S*.*" -File | ForEach-Object {
$series = $_.Name -replace '.*(S\d{2}).*', '$1'
# create the target path inside the same directory and create if needed
$destination = Join-Path -Path $_.DirectoryName -ChildPath $series
if (!(Test-Path -Path $destination -PathType Container)) {
$null = New-Item -Path $destination -ItemType Directory
}
# move the file to the new path
$_ | Move-Item -Destination $destination -Force
}
This works
\192.168.1.2\Video\PodCasts\S01\MyPodcast S01E01.mp4
I would like to adjust to do
\192.168.1.2\Video\PodCasts\MyPodcast S01\MyPodcast S01E01.mp4
I dont see where in $destination to adjust it
Your code can be simplyfied by getting the list of files in the shared directory \\192.168.1.2\PodCasts and looping through them in a ForEach-Object loop.
Then, get the series number S01, S02 etc. from the file's name, create a folder in the shared directory \\192.168.1.2\PodCasts with that series name and move the file there:
Get-ChildItem -Path '\\192.168.1.2\PodCasts' -Filter 'MyPodcast S*.mp4' -File | ForEach-Object {
$series = $_.Name -replace '.*(S\d{2}).*', '$1'
# or do something like this: $series = ($_.BaseName -split ' ',2)[1].Substring(0,3)
# create the target path inside the same PodCasts directory and create if needed
$destination = Join-Path -Path $_.DirectoryName -ChildPath $series
if (!(Test-Path -Path $destination -PathType Container)) {
$null = New-Item -Path $destination -ItemType Directory
}
# move the file to the new path
$_ | Move-Item -Destination $destination -Force
}
Before:
\\192.168.1.2\PODCASTS
MyPodcast S01E01.mp4
MyPodcast S01E02.mp4
MyPodcast S02E01.mp4
MyPodcast S02E02.mp4
MyPodcast S02E03.mp4
MyPodcast S03E01.mp4
MyPodcast S03E02.mp4
MyPodcast S03E03.mp4
After:
\\192.168.1.2\PODCASTS
+---S01
| MyPodcast S01E01.mp4
| MyPodcast S01E02.mp4
|
+---S02
| MyPodcast S02E01.mp4
| MyPodcast S02E02.mp4
| MyPodcast S02E03.mp4
|
\---S03
MyPodcast S03E01.mp4
MyPodcast S03E02.mp4
MyPodcast S03E03.mp4
P.S. In your example filelist, file 'MyPodcast S01E02.mp4' is doubled
Firstly, never run destructive (New, create, rename, move, delete, update, clear...) code without a validation step.
Is this what you are after?
Clear-Host
#(
'D:\PodCasts\MyPodcast S01E01.mp4',
'D:\PodCasts\MyPodcast S01E02.mp4',
'D:\PodCasts\MyPodcast S01E02.mp4',
'D:\PodCasts\MyPodcast S02E01.mp4',
'D:\PodCasts\MyPodcast S02E02.mp4',
'D:\PodCasts\MyPodcast S02E03.mp4',
'D:\PodCasts\MyPodcast S03E01.mp4',
'D:\PodCasts\MyPodcast S03E02.mp4',
'D:\PodCasts\MyPodcast S03E03.mp4'
) |
ForEach {
$SourceFolder = [regex]::matches($PSItem, '.*\\PodCasts').Value
$DestinationFolder = [regex]::matches($PSItem, 'S0\d').Value
If (Test-Path -Path "$SourceFolder\$DestinationFolder")
{
Write-Verbose -Message "Processing $SourceFolder\$DestinationFolder" -verbose
Move-Item -Path $PSItem -Destination $destinationFolder -WhatIf
}
Else
{
Write-Warning -Message "
Attempt to move a file to nonexistent folder, failed.
Creating the resource: $SourceFolder\$DestinationFolder
"
New-Item $DestinationFolder -ItemType Directory -WhatIf
Move-Item -Path $PSItem -Destination $destinationFolder -WhatIf
}
}
# Results
<#
WARNING:
Attempt to move a file to nonexistent folder, failed.
Creating the resource: D:\PodCasts\S01
What if: Performing the operation "Create Directory" on target "Destination: D:\Scripts\S01".
What if: Performing the operation "Move File" on target "Item: D:\PodCasts\MyPodcast S01E01.mp4 Destination: D:\Scripts\S01".
...
#>
If the folder already exists
# Results
<#
VERBOSE: Processing D:\PodCasts\S01
What if: Performing the operation "Move File" on target "Item: D:\PodCasts\MyPodcast S01E01.mp4 Destination: D:\Scripts\S01".
VERBOSE: Processing D:\PodCasts\S01
What if: Performing the operation "Move File" on target "Item: D:\PodCasts\MyPodcast S01E02.mp4 Destination: D:\Scripts\S01".
...
WARNING:
Attempt to move a file to nonexistent folder, failed.
Creating the resource: D:\PodCasts\S02
What if: Performing the operation "Create Directory" on target "Destination: D:\Scripts\S02".
What if: Performing the operation "Move File" on target "Item: D:\PodCasts\MyPodcast S02E01.mp4 Destination: D:\Scripts\S02".
#>

Powershell exclude pdf extension files

I am trying to run powershell script to separate files of all extensions by iterating all the subfolders and creating a subfolder attachments at depth 3 except pdf but it is not working. Can someone help me out by pointing what I am doing incorrectly in script.
Thanks in advance
ForEach($Folder in (Get-ChildItem -Directory .\*\*\*)){
echo "Done"
Get-ChildItem -path $Folder -Exclude *.pdf | Move-Item -Destination $Folder\Attachments -ErrorAction Stop
}
I'd suggest adding the -WhatIf parameter to Move-Item to troubleshoot what that command is actually trying to do. It will probably be quite clear from the output that it's not doing what you think it's doing.
My guess is the problem is the fact that $Folder contains a DirectoryInfo item. The default string expansion for that is just the name, and you probably want the FullName.
Try:
ForEach($Folder in (Get-ChildItem -Directory .\*\*\*)){
echo "Done"
Get-ChildItem -path $Folder -Exclude *.pdf | Move-Item -Destination "$($Folder.FullName)\Attachments" -ErrorAction Stop
}
However, it's not clear from your question what you're actually trying to accomplish, primarily because "it doesn't work" is not a problem description. I'm not sure where the attachments folder is supposed to be.
Extending from my comment. Try something like this.
'PowerShell -Whatif or Confirm'
$Destination = 'D:\temp'
(Get-ChildItem -Directory -Recurse -Exclude '*.pdf' -Depth 3) |
ForEach-Object {
Write-Verbose -Message "Processing: $PSItem" -Verbose
# Remove the whatIf after you validate the results of the move and run it again.
Try {Move-Item -Path $PSItem.FullName -Destination "$Destination\Attachments" -ErrorAction Stop -WhatIf}
Catch
{
Write-Warning -Message "Error processing request"
$PSItem.Exception.Message
}
}
# Results
<#
VERBOSE: Processing: D:\Scripts\.vs
What if: Performing the operation "Move Directory" on target "Item: D:\Scripts\.vs Destination: D:\temp\Attachments".
VERBOSE: Processing: D:\Scripts\.vs\Scripts
What if: Performing the operation "Move Directory" on target "Item: D:\Scripts\.vs\Scripts Destination: D:\temp\Attachments".
VERBOSE: Processing: D:\Scripts\.vs\Scripts\v16
What if: Performing the operation "Move Directory" on target "Item: D:\Scripts\.vs\Scripts\v16 Destination: D:\temp\Attachments".
...
#>
If you want to see what is happening under the covers as part of your check, you can do this.
Trace-Command
# Review what the command expression is doing
Trace-Command -Name metadata,parameterbinding,cmdlet -Expression {
$Destination = 'D:\temp'
(Get-ChildItem -Directory -Recurse -Exclude '*.pdf' -Depth 3) |
ForEach-Object {
Write-Verbose -Message "Processing: $PSItem" -Verbose
# Remove the whatIf after you validate the results of the move and run it again.
Try {Move-Item -Path $PSItem.FullName -Destination "$Destination\Attachments" -ErrorAction Stop -WhatIf}
Catch
{
Write-Warning -Message "Error processing request"
$PSItem.Exception.Message
}
}
} -PSHost

I want to exclude a sub folder from a recursive search and copy

I want to look in directory "x" recursively for file type "y" excluding sub directory "m" and copy files to sub directory "m"
im trying this
$TargetDir = read-host "Please enter target Dir"
$sourceDir = read-host "Please enter source Dir"
$format = read-host "Format to look for"
Get-ChildItem -Path $sourceDir -Recurse -Filter $format | where FullName -Not -Like $TargetDir |
Copy-Item -Destination $TargetDir
You better try use foreach cycle. Here is example for searching for photo and video.
# Making an array of all files from Get-ChildItem
$searchItems = Get-ChildItem -Path $sourceDir -Recurse -Include *.mp4,*.jpg,*.jpeg | where FullName -notlike $TargetDir
# Copy files one by one in cycle
foreach($currentFile in $searchItems) {
Copy-Item -Path $currentFile.Fullname -Destination $TargetDir
}
I figured it out, But it would be great to have some things explained
CODE:
$TargetDir = read-host "Please enter target Dir"
$sourceDir = read-host "Please enter source Dir"
$format = read-host "Format to look for"
$folders = Get-ChildItem -Path $sourceDir -Recurse -filter $format | Where
{($_.PSIsContainer) -and ($TargetDir -notcontains $_.Name)}
foreach ($f in $folders){
Write-Host "These Files will be copied: $f"
Copy-Item - path $sourceDir$f -Destination $TargetDir\$f -Recure -Force}
I dont understand :
Where {($_.PSIsContainer) -and ($TargetDir -notcontains $_.Name)}
and why
Write-Host "These Files will be copied: $f"
does not show up when ran
This...
[Write-Host "These Files will be copied: $f"]
... is because you are doing this is a loop, and Write-Host clear the buffer. Except for when you need colorized screen output, Write-Host is mostly useless. Well, except for in some formatting use cases. Generally just don't use it. Even the PowerShell author says so.
Output to the screen is the PowerShell default unless you tell it otherwise. The better option to Write-Host is Write-Output, or Out-Host or Tee-Object. See the help files on their particulars.
Get-ChildItem has several switches and if you just want directories, there's a switch for that and -Exclude for well, you know.
# Get specifics for a module, cmdlet, or function
(Get-Command -Name Get-ChildItem).Parameters
(Get-Command -Name Get-ChildItem).Parameters.Keys
# Results
<#
...
Exclude
...
Directory --- (This is what that $_.PSIsContainer/$PSItem.PSIsContainer this is doing, but that is legacy stuff.)
...
#>
Get-help -Name Get-ChildItem -Examples
Get-help -Name Get-ChildItem -Full
Get-help -Name Get-ChildItem -Online
About Objects --- Objects in Pipelines
Thus...
# Get directory names only and return only the full UNC
(Get-ChildItem -Directory -Path 'D:\SourceFolder'-Recurse).FullName
# Results
<#
D:\SourceFolder\est
D:\SourceFolder\here
D:\SourceFolder\hold
D:\SourceFolder\TargetFolder
#>
# Just like the above, but exclude the TargetFolder
(Get-ChildItem -Directory -Path 'D:\SourceFolder' -Exclude 'TargetFolder' -Recurse).FullName
# Results
<#
D:\SourceFolder\est
D:\SourceFolder\here
D:\SourceFolder\hold
#>
So, a small refactoring of your code.
# Recursively get all text files, in all directories, except one for copy action
Get-ChildItem -Path 'D:\SourceFolder' -Filter '*.txt' -Exclude 'TargetFolder' -Recurse |
ForEach {
"These Files will be copied: $($PSItem.FullName)"
# Remove the -whatIf for the actual run.
Copy-Item -Path $PSItem.FullName -Destination 'D:\SourceFolder\TargetFolder' -Recurse -WhatIf
}
# Results
<#
These Files will be copied: D:\SourceFolder\here\mytest - Copy.txt
What if: Performing the operation "Copy File" on target "Item: D:\SourceFolder\here\mytest - Copy.txt Destination: D:\SourceFolder\TargetFolder\mytest - Copy.txt".
These Files will be copied: D:\SourceFolder\here\mytest.txt
What if: Performing the operation "Copy File" on target "Item: D:\SourceFolder\here\mytest.txt Destination: D:\SourceFolder\TargetFolder\mytest.txt".
These Files will be copied: D:\SourceFolder\hold\awél.txt
What if: Performing the operation "Copy File" on target "Item: D:\SourceFolder\hold\awél.txt Destination: D:\SourceFolder\TargetFolder\awél.txt".
#>
Of course, you need to tweak the above yo use your variables, and take out all the comments. Commenting is a good thing, but over commenting is just as bad as under commenting or bad/unintelligible commenting. All of which many have their preferences.
My rules:
Don't comment on the obvious, where the code speaks for itself.
Don't use shorthand stuff only you know or remember.
Write in the plain straight forward localized language.
Write for those that don't know you or shorthand stuff and will follow you or who may use/review your code; who can and often are new to this stuff.

How to merge / 'flatten' a folder structure using PowerShell - recursive

I'm looking for help restructuring a large number of files within many subfolders.
Example source:
folderX
aaa.txt
bbb.txt
folderY
ccc.txt
folderZ
ddd.txt
eee.txt
Ideal result:
folderX_aaa.txt
folderX_aaa.txt
folderX_bbb.txt
folderY_ccc.txt
folderY_folderZ_ddd.txt
eee.txt
I hope that makes sense! I'm using Plex to manage some media and it doesn't like subfolders for certain uses (eg featurettes directory).
I'd like to use PowerShell because I'm already kinda familiar with it - but any techniques or suggestions are welcome.
Thanks in advance :)
Here's a single-pipeline solution:
$targetDir = Convert-Path '.' # Get the current (target) directory's full path.
Get-ChildItem -LiteralPath $targetDir -Directory | # Loop over child dirs.
Get-ChildItem -Recurse -File -Filter *.txt | # Loop over all *.txt files in subtrees of child dirs.
Move-Item -Destination { # Move to target dir.
# Construct the full target path from the target dir.
# and the relative sub-path with path separators replaced with "_" chars.
Join-Path $targetDir `
($_.Fullname.Substring($targetDir.Length + 1) -replace '[/\\]', '_')
} -Whatif
-WhatIf previews the move operations; remove it to perform actual moving.
Regex [/\\] matches / or \ as the path separator, so as to make the solution cross-platform.
while it would have been nice if you had shown what you have tried, i got interested ... [grin]
what the code does ...
sets some constants
creates some files & dirs to work with
grabs the files that match the target location & type
skips any file that is already in the $TopDir
derives the old file & dir names
uses the above to make a new full file name
moves the items
this only shows what WOULD happen. remove the -WhatIf to do it for real.
please note that the code DOES NOT check to see if there are same-named files in the destination.
the code itself ...
$TopDir = "$env:TEMP\PlexStuff"
$Delimiter = '_-_'
$Filter = '*.txt'
#region >>> create some files to test with
# remove this region after you have tested it
#(
[System.IO.FileInfo]"$TopDir\OneThing\aaa.txt"
[System.IO.FileInfo]"$TopDir\OneThing\bbb.txt"
[System.IO.FileInfo]"$TopDir\TwoThing\aaa.txt"
[System.IO.FileInfo]"$TopDir\ThreeThing\ccc.txt"
[System.IO.FileInfo]"$TopDir\ThreeThing\ddd.txt"
[System.IO.FileInfo]"$TopDir\eee.txt"
) |
ForEach-Object {
# the "$Null =" suppresses unwanted output from the commands
$Null = mkdir $_.Directory.FullName -Force -ErrorAction SilentlyContinue
$Null = New-Item -Path $_.Directory.FullName -Name $_.Name -ItemType File -ErrorAction SilentlyContinue
}
#endregion >>> create some files to test with
$FileList = Get-ChildItem -LiteralPath $TopDir -File -Recurse -Filter $Filter
foreach ($FL_Item in $FileList)
{
# skip files that are in the TopDir
if (-not ($FL_Item.DirectoryName -eq $TopDir))
{
$OldFileName = $FL_Item.Name
$OldDirName = $FL_Item.DirectoryName
$NewFileName = ($OldDirName, $OldFileName) -join $Delimiter
Move-Item -LiteralPath $FL_Item.FullName -Destination $NewFileName -WhatIf
}
}
output [slightly reformatted for readability] ...
What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\OneThing\aaa.txt
Destination: C:\Temp\PlexStuff\OneThing_-_aaa.txt".
What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\OneThing\bbb.txt
Destination: C:\Temp\PlexStuff\OneThing_-_bbb.txt".
What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\ThreeThing\ccc.txt
Destination: C:\Temp\PlexStuff\ThreeThing_-_ccc.txt".
What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\ThreeThing\ddd.txt
Destination: C:\Temp\PlexStuff\ThreeThing_-_ddd.txt".
What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\TwoThing\aaa.txt
Destination: C:\Temp\PlexStuff\TwoThing_-_aaa.txt".

how to recursively change name across several folders in powershell

i am trying to recursively rename files in a folder that contains a bunch of folders.
to better clarify, i have a parent folder with 10 child folders, each of the 10 child folders have 17 sound files.
i need to rename each of the 17 sound file 1,2,3...17
i managed to come up with the following code (which for now, writes the file name instead of actually changing it)
$files = gci -Path "D:\PARENT_FOLDER" -Recurse
$i = 1
foreach ($file in $files) {
$newName = 0
1..$i | % {$newName = $newName+1}
$i++
Write-Host "name is " $newName
}
but i cant figure out how to make it reset the count between folders.
right now the code outputs names from 1 to 180...
can anybody help me figure this out?
thanks in advance
ok so after a long day at work, i got home and tackled this from scratch and came up with a simpler solution:
i just nested "foreach" loops one inside the other to cycle thru all the folders inside the parent folder and all the files inside each folder.
if anybody is interested here is the code:
$path = "MASTER FOLDER PATH"
$list = Get-ChildItem -Path $path -Directory
foreach ($folder in $list)
{
Write-Host "working on Directory" $folder.FullName -ForegroundColor Green
foreach ($files in $folder)
{$files = Get-ChildItem -Path $path\$folder
$i=1
foreach ($file in $files) {
$newName = 0
1..$i | % {$newName = $newName+1}
$i++
Write-Host "Changing file " $file.FullName -NoNewline -ForegroundColor Yellow
Write-Host " ..." -ForegroundColor Yellow
Rename-Item -Path $file.FullName -NewName $newName
}
}
}
i appreciate your help.
:)
I'd recommend leaving out the -Recurse part for now and to do the recursive things manually (AKA in a loop).
$list = Get-ChildItem -Path $path -Name -Directory
for ($i=0;$i -le $list.Length-1;$i++) {
$list[$i] = $path + '\' + $list[$i]
}
Now you have an array of folders, which you can now individually adress.
$path = "(path)"
$list = Get-ChildItem -Path $path -Name -Directory
for ($i=0;$i -le $list.Length-1;$i++) {
$list[$i] = $path + '\' + $list[$i]
$temp = Get-ChildItem -Path $list[$i] -Name
for ($h=0;$h -le $temp.Length-1;$h++) {
$temp[$h] = $list[$i] + $temp[$h]
Rename-Item -LiteralPath $temp[$h] -NewName $h
}}
Thats what I came up with, hope it was helpful.
Here's another way. I don't see how to recurse and reset the number for each folder. This way I'm doing one folder at a time. Foreach with two script blocks will treat the first one as a "begin" script block.
foreach($dir in get-childitem parent) {
get-childitem parent\$dir | foreach {$i = 1} { rename-item $_.fullname $i -whatif; $i++ }
}
What if: Performing the operation "Rename File" on target "Item: C:\users\js\parent\foo\a Destination: C:\users\js\parent\foo\1".
What if: Performing the operation "Rename File" on target "Item: C:\users\js\parent\foo\b Destination: C:\users\js\parent\foo\2".
What if: Performing the operation "Rename File" on target "Item: C:\users\js\parent\foo\c Destination: C:\users\js\parent\foo\3".
What if: Performing the operation "Rename File" on target "Item: C:\users\js\parent\foo2\a Destination: C:\users\js\parent\foo2\1".
What if: Performing the operation "Rename File" on target "Item: C:\users\js\parent\foo2\b Destination: C:\users\js\parent\foo2\2".
What if: Performing the operation "Rename File" on target "Item: C:\users\js\parent\foo2\c Destination: C:\users\js\parent\foo2\3".