We have a script running daily that removes old files and directories from an area that people use to transfer data around. Everything works great except for one little section. I want to delete a folder if it's older than 7 days and it's empty. The script always shows 1 file in the folder because of the thumbs.db file. I guess I could check to see if the one file is thumb.db and if so just delete the folder but I'm sure there is a better way.
$location = Get-ChildItem \\dropzone -exclude thumbs.db
foreach ($item in $location) {
other stuff here going deeper into the tree...
if(($item.GetFiles().Count -eq 0) -and ($item.GetDirectories().Count -eq 0)) {
This is where I delete the folder but because the folder always has
the Thumbs.db system file we never get here
}
}
$NumberOfFiles = (gci -Force $dir | ?{$_ -notmatch "thumbs.db"}).count
You can try the get-childitem -exclude option where all files/items in your directory will be
counted except those that end in db:
$location = get-childitem -exclude *.db
It also works out if you specify the file to exclude, in this case thumbs.db
$location = get-childitem -exclude thumb.db
Let me know if this works out.
Ah, I also just noticed something,
$location = get-childitem -exclude *.db
Will only handle .db items in the location directory, if you're going deeper into the tree (say from your GetFiles() and GetDirectories() methods) then you may still find a thumb.db. Hence you'll have to add the exclude option in these methods to ignore thumbs.db.
So, for example in your $item.getFiles() method, if you use get-childitem you will have to specify the -exclude option as well.
Sorry, I should have read your question more closely.
Use this method to provide a exclusion list in the form of a simple text file to exclude specific files or extensions from your count:
$dir = 'C:\YourDirectory'
#Type one filename.ext or *.ext per line in this txt file
$exclude = Get-Content "C:\Somefolder\exclude.txt"
$count = (dir $dir -Exclude $exclude).count
$count
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 days ago.
Improve this question
I'm not a beginner to scripting with PowerShell and have discovered just how powerful and amazing it truly is. I get confused with some things though so I am here seeking help with a few things with the script that I'm in the process of creating. My script manages a few things. It:
• prompt user to select a directory
• recursively moves files that are many levels deep into the parent folder
• deletes all empty folders after the move
•renames the parent folders by removing periods and other "illegal" characters because the program that uses these files will crash if there are any characters besides numbers or letters.
• renames the files to the parent's name.
• exits when finished
The files don't have a file format extension, they're approx 32 characters long and are alphanumeric.
Unfortunately, the script cannot make it past the first step (moving the files) if it is placed in a directory outside of the one that contains the folders and files. If I place it in the root of the directory containing said files and folders, it works flawlessly. If it is ran in another directory containing other files, it will work with the files and folders there after finishing the 1st step which is set using $RootPath, the rest of the script is not using $RootPath and I need to figure out how to edit the code I have to utilize it.
However no matter what I do, I fail. I know I can just run it from the directory containing the files that need to be moved but I intend to release this on a forum that I frequent and want to make sure it is safe for those that use it. ie: I don't want their system getting messed up through carelessness or ignorance.
Full Disclosure: I'm not good at writing code on my own, I find chunks of code offered in forums, test and if it accomplishes what I need, I tweak it to work with my intended use. Most of this code I found here.
How can I get the last ⅔ of my script to use my $RootPath instead of the script's residing directory? I have tried a few things but end up breaking it's functionally and the thing is, in my mind, I see why it's not working but reading the code is where I have a Patrick-Star-drooling moment. This is when I get overwhelmed and take a break or focus on something else that I do understand. I know I need to make the rest of my code utilize $RootPath that gets set when selecting a directory but I can't figure out how to get it to use it.
Additionally, I would like the final step to append "_1" to the file name when there is a naming conflict. I can't seem to figure out how to get this step to carry over from the first step.
Here is a pastebin link of my script. It is a bit long, I have also pasted the code in case that is preferred.
# You need this to use System.Windows.MessageBox
Add-Type -AssemblyName 'PresentationFramework'
Add-Type -AssemblyName System.Windows.Forms
$continue = 'No'
$caption = "'Bulk File Renamer Script' by MyLegzRwheelz."
$message = "Have you read the ENTIRE disclaimer (from the very top, I know, it is a lot) in the console window along the instructions provided and do you agree that you are responsible for your own negligence and anything that can go wrong IF YOU DO NOT FOLLOW MY INSTRUCTIONS PRECISELY? If so, then click 'Yes' to proceed, 'No' to exit."
$continue = [System.Windows.MessageBox]::Show($message, $caption, 'YesNo');
if ($continue -eq 'Yes') {
$characters = "?!'._" # These are the characters the script finds and removes
$regex = "[$([regex]::Escape($characters))]"
$filesandfolders = Get-ChildItem -recurse | Where-Object {$_.name -match $regex}
$browser = New-Object System.Windows.Forms.FolderBrowserDialog
$null = $browser.ShowDialog()
$RootPath = $browser.SelectedPath
# Get list of parent folders in root path
$ParentFolders = Get-ChildItem -Path $RootPath | Where-Object {$_.PSIsContainer}
# For each parent folder get all files recursively and move to parent, append number to file to avoid collisions
ForEach ($Parent in $ParentFolders) {
Get-ChildItem -Path $Parent.FullName -Recurse | Where-Object {!$_.PSIsContainer -and ($_.DirectoryName -ne $Parent.FullName)} | ForEach-Object {
$FileInc = 1
Do {
If ($FileInc -eq 1) {$MovePath = Join-Path -Path $Parent.FullName -ChildPath $_.Name}
Else {$MovePath = Join-Path -Path $Parent.FullName -ChildPath "$($_.BaseName)($FileInc)$($_.Extension)"}
$FileInc++
}
While (Test-Path -Path $MovePath -PathType Leaf)
Move-Item -Path $_.FullName -Destination $MovePath
}
}
$filesandfolders | Where-Object {$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
Rename-Item -path $_.Fullname -newname $New -passthru
}
# For this to work, we need to temporarily append a file extension to the file name
Get-ChildItem -File -Recurse | where-object {!($_.Extension)} | Rename-Item -New {$_.basename+'.ext'}
# Removes alphanumeric subdirectories after moving renamed game into the parent folder
Get-ChildItem -Recurse -Directory | ? { -Not ($_.EnumerateFiles('*',1) | Select-Object -First 1) } | Remove-Item -Recurse
# Recursively searches for the files we renamed to .ext and renames it to the parent folder's name
# ie: "B2080E9FFF47FE2DA382BD55EDFCA2152078AEBD58.ext" becomes "0 day Attack on Earth" and will be
# found in the directory of the same name.
ls -Recurse -Filter *.ext | %{
$name = ([IO.DirectoryInfo](Split-Path $_.FullName -Parent)).Name
Rename-Item -Path $_.FullName -NewName "$($name)"
}
} else {
{Exit}
}
I have tried using $ParentFolders in varying places so that it uses $RootPath as the working directory. I have also tried copy/pasting the "file inc" part in the final step but it is not working. To test this out, create folder, make this your root folder. Within that folder, create additional folders with multiple subfolders and a file with no extension, just create .txt and remove the extension then run the script from the newly created root folder.
Do not run this in a directory with files you care about. This why I am trying to get the rest of the code to use only the directory set at launch. To test it to see if it is working regardless of the scripts location, place the script in another folder then run it. When the explorer dialog pops up (after clicking yes), select this directory. If you place it in the root directory then run it, it works as it should but not in any other director, which is the desired result, to run and work to completion, regardless of the location of the script.
Here is code to add _1 to filename
$filename = "abcdefg.csv"
$lastIndex = $filename.LastIndexOf('.')
$extension = $filename.Substring($lastIndex)
$filename = $filename.Substring(0,$lastIndex)
Write-Host "filename = " $filename ",extension = " $extension
$filename = $filename + "_1" + $extension
$filename
It's my first time using a custom Powershell script and I think I might be missing something. Everything works, except it doesn't copy subfolders and their content.
$sourcePath = 'C:\Users\User\Desktop\test\test1'
$destinationPath = 'C:\Users\User\Desktop\test\test2'
$files = Get-ChildItem -Path $sourcePath -Recurse
$filecount = $files.count
$i=0
Foreach ($file in $files) {
$i++
Write-Progress -activity "Moving files..." -status "($i of $filecount) $file" -percentcomplete (($i/$filecount)*100)
# Determine the absolute path of this object's parent container. This is stored as a different attribute on file and folder objects so we use an if block to cater for both
if ($file.psiscontainer) {$sourcefilecontainer = $file.parent} else {$sourcefilecontainer = $file.directory}
# Calculate the path of the parent folder relative to the source folder
$relativepath = $sourcefilecontainer.fullname.SubString($sourcepath.length)
# Copy the object to the appropriate folder within the destination folder
copy-Item $file.fullname ($destinationPath + $relativepath)
}
i'm sure it's something to do with Path instead of Root or the Recurse option that's not used corretly.
If someone could help me it would be really appreciated.
Thank you!
To copy recursively, use
Copy-Item -Path <src> -Destination <dest> -Recurse
See Official documentation.
This may very well work for you.
As for walking through a previously built list of source files and copying one-by-one as you did, it should work. I did not check/debug the code you put together.
If you actually need this walk through, simply debug your code by printing $files and the source and target of your copy-Item, this will tell you the source of problems. Please post this output in the OP.
You may have to use the option -Directory for Get-ChildItem, or class [System.IO.Path].
Right in the beginning I should note that I am a bloody beginner because I can't attend it classes in my grade.
I want to create a PowerShell script which will copy everything from
C:\Users\Robert\Desktop\test(lots of folders)
to
C:\Users\Robert\Desktop\neu(lots of folders with the exact same names as above)\price
As an absolute beginner I thought that it will be ok to replace the variable folder name with $_Name because it is the same name in both but I am obviously wrong and don't know why.
Here is my attempt
Copy-Item "C:\Users\Robert\Desktop\test\$_name\*" -Destination "C:\Users\Robert\Desktop\neu\$_Name\price" -Recurse
It is copying something but in one package in a new folder in "neu".
I can't avoid creating this script because it would take me at least two or three days to do it by hand.
I am also sorry for my poor English skills
Thank you
the $_ represents the current pipeline item. i don't see a pipeline in there ... [grin]
the following works by grabbing every file in the source dir & its subdirs, and copying that structure to the destination dir. it uses Splatting to structure the parameters neatly.
$SourceDir = "$env:TEMP\Apps - Copy"
$DestDir = "$env:TEMP\Apps - Copy - Two"
$CI_Params = #{
LiteralPath = $SourceDir
Destination = $DestDir
Force = $True
Recurse = $True
}
Copy-Item #CI_Params
If my understanding is correct:
$src = 'C:\Users\Robert\Desktop\test'
$dst = 'C:\Users\Robert\Desktop\neu\{0}\price'
Get-ChildItem $src -Directory | ForEach-Object {
Copy-Item -Path "$($_.FullName)\*" -Destination ($dst -f $_.BaseName) -Recurse -Force -WhatIf
}
Remove -WhatIf to actually do it.
I have this below code which is used to copy DBUpgrade folder to another location, but it copies the hidden folder present inside it as well.
$Demo="F:\source\DBUpgrade"
$new="F:\Test"
Copy-Item $demo $new -Recurse -Force
Is there any command by which I can exclude the hidden folder and one more thing the name of the hidden folder is $tf because of which i am not able to use remove-item command to remove the folder itself as another option.
You could do a
$list = get-childitem $demo $new -recurse -attributes !H | select fullname
And than a foreach loop
foreach ($file in $list){
Copy commands go here
}
Mind you copy commands will require some finesse to get it in the right folder and may not be worth the hassle
You'll have to trim the path out and do checks if it's $demo or $new to get it to the correct folder.
Get-Childitem by default doesn't show hidden items
This has been sort of asked, but none of the questions I've found have quite answered what I'm looking to do. I'm working with PowerShell (brand new to it) to write a script that will search for subdirectories within a directory and move those to a designated directory if found.
My problem lies within the following code:
$Folders = C:\Users\temp
$MoveFolders = Test-Path $Folders -PathType Container
Write-Host $MoveFolders
#I'm writing this with ISE, so I'm using write-host to view output for testing.
The problem I'm running into is that every time this code is ran, it returns true, even when there are no folders within the temp directory. I've tried it with about every conceivable way I can imagine, and tested with get-childitem piped with a where-object, but I want to only execute the move if a subdirectory is present.
The idea behind it is that, if a user somehow adds a file or folder to this specific one, it will be moved when the task scheduler runs the script.
EDIT
Redirecting my question; It always returns true, and a couple of people have pointed out that what I have written will test the temp folder itself; so is there a way to test for any subfolders and store it as a boolean value, which I can then pass to an if statement that will finish the move process?
I believe this is what you want to do.
#get the folders/subfolders from the directory
$folders = Get-ChildItem C:\Users\temp -Recurse -Directory
#loop through the folders
foreach($folder in $folders) {
#copy the the folder(s) and item(s) within to the destination
Copy-Item -Path $folder.FullName -Destination C:\test -Recurse
}
Here is the updated answer since you edited your question.
$items = Get-ChildItem -Path C:\Users\mkrouse\Desktop\test -Directory -Recurse
#if items is equal to null, then there are no subfolders so assign the boolean to true
if($items -eq $null) {
[bool]$NoSubfolders = $true;
} else {
[bool] $NoSubfolders = $false;
}
Your code tests whether "c:\users\temp" is a folder - which is always true. You need to look for folders within "c:\users\temp". One approach:
$Folders = "C:\temp"
$MoveFolders = Get-ChildItem -Path $folders -Directory
Write-Host $MoveFolders.Count
$MoveFolders now contains a list of all folders within "c:\users\temp". Now you have a list of folders to be moved.