Is there a way in Powershell to not search in multiple folders - powershell

I want to search for all files in a PC but I want to exclude some of the folders.
I'm currently using Where-Object { ($_.FullName -notmatch $excludepath) but the problem with this is it looks up in those paths and then filters it. I want my program to not look up in some paths at all because it takes up a lot of time!
Edit: This is the code I'm working with. I want to search a PC for either all the files or with some specific filters like files with specific name, extension and also give the option to exclude a path entirely. This code does all that but while excluding a path it searches in the path and then filters out using Where-Object { ($_.FullName -notmatch $excludepath) . Because C drive is so big I want my program to not look up in the certain multiple path mentioned rather than searching in them and then filtering.
$Filename = "img"
$IncludeExt = "*.jpeg"
$excludepath = "^C:\\Windows" ,"^C:\\Program Files"
$GCIArgs = #{Path = $Drives.Root
Recurse = $True
}
If ($Null -ne $IncludeExt) {
$GCIArgs.Add("Include",$IncludeExt)
}
Get-ChildItem #GCIArgs | Where-Object { ($_.FullName -notmatch $excludepath) -and ($Ignore -notcontains $_.Extension) -and ($_.BaseName -match $Filename )} |
foreach{
$Item = $_.Basename
$Path = $_.FullName
$Type = $_.Extension
$Modified=$_.LastWriteTime
$Age = $_.CreationTime
$Type = &{if($_.PSIsContainer){"Folder"}else{$_.Extension}}
$Path | Select-Object #{n="Name";e={$Item}},
#{n="Created";e={$Age}},
#{n="filePath";e={$Path}},
#{n="Modified";e={$Modified}},
#{n="Folder/File";e={$Type}}
}| Export-Csv D:\SF.csv -NoTypeInformation

try trhis:
$dirtoexclude=#(
'C:\temp\sqldeveloper\sqldeveloper\svnkit\licenses',
'C:\temp\sqldeveloper\sqldeveloper\sqldeveloper\lib' ,
'C:\temp\sqldeveloper\sqldeveloper\sqldeveloper\extensions\oracle.olap',
'C:\temp\sqldeveloper'
)
#method 1 : if you want exclude specific directory
get-childitem "c:\temp" -Recurse | where DirectoryName -notin $dirtoexclude
#method 2 : if you want exclude specific directory and sub directory
get-childitem "c:\temp" -Recurse | foreach{
$Current=$_
#search if current directory element start by one of directory to exclude
$founded=$dirtoexclude | where {$Current.DirectoryName -like "$_*"} | select * -First 1
#not start by directory to exclude, send element to output
if (!$founded)
{
$Current
}
}

Related

My script doesnt work when I change the object property from "LastWriteTime" to "CreationTime", it just deletes everything?

Ive been running around like crazy lately with this script that Im trying to modify it to suit my needs. I recently found out that deleting the files using "LastWriteTime" is not what Im after..
What I need my script to do is to delete the files that are older than 30 days using the "CreationTime" property, the problem is that after I modify the script to use this it deletes the entire folder structure?
How can this small modification change the behavior of the entire script?
This is what Im using:
$limit = (Get-Date).AddDays(-30)
$del30 = "D:\CompanyX_ftp\users"
$ignore = Get-Content "C:\Users\UserX\Documents\Scripts\ignorelist.txt"
Get-ChildItem $del30 -Recurse |
Where-Object {$_.CreationTime -lt $limit } |
Select-Object -ExpandProperty FullName |
Select-String -SimpleMatch -Pattern $ignore -NotMatch |
Select-Object -ExpandProperty Line |
Remove-Item -Recurse
So if I were to replace the "CreationTime" property with "LastWriteTime" the script will run and do what its supposed to but if I use "CreationTime" it just deletes everything under the folder structure including the folders themselves and the paths that its supposed to ignore.
UPDATE: The script is working for me now for the actual deletion of the files but for the script that Im using to just get a report on the actual files that the script will delete is actually including the paths of the ignorelist.txt file?
Please see below script:
$limit = (Get-Date).AddDays(-30)
$del30 = "D:\CompanyX_ftp\users"
#Specify path for ignore-list
$ignore = Get-Content "C:\Users\UserX\Documents\Scripts\ignorelist.txt"
Get-ChildItem $del5 -File -Recurse |
Where-Object {$_.CreationTime -lt $limit } |
Select-Object -ExpandProperty FullName |
Select-String -SimpleMatch -Pattern $ignore -NotMatch |
Select-Object -ExpandProperty Line |
Get-ChildItem -Recurse | Select-Object FullName,CreationTime
ignorelist.txt sample data:
D:\CompanyX_ftp\users\ftp-customerA\Customer Downloads
D:\CompanyX_ftp\users\ftp-customerB\Customer Downloads
D:\CompanyX_ftp\users\ftp-customerC\Customer Downloads
D:\CompanyX_ftp\users\ftp-customerD\Customer Downloads
D:\CompanyX_ftp\users\ftp-customerE\Customer Downloads
D:\CompanyX_ftp\users\ftp-customerF\Customer Downloads
D:\CompanyX_ftp\users\ftp-customerG\Customer Downloads
D:\CompanyX_ftp\users\ftp-customerH\Customer Downloads\
Any ideas on why its including the paths that I have mentioned on the ignorelist.txt? (I will also provide an image for better illustration).
Thanks in advance for any help or guidance with this.
//Lennart
I see two problems with the updated code:
Duplicate recursion. First Get-ChildItem iterates over contents of directory recursively. Later in the pipeline another recursive iteration starts on items returned by the first Get-ChildItem, causing overlap.
When filtering by $ignore, only paths that exactly match against the $ignore paths are being ignored. Paths that are children of items in the ignore list are not ignored.
Here is how I would do this. Create a function Test-IgnoreFile that matches given path against an ignore list, checking if the current path starts with any path in the ignore list. This way child paths are ignored too. This enables us to greatly simplify the pipeline.
Param(
[switch] $ReportOnly
)
# Returns $true if $File.Fullname starts with any path in $Ignore (case-insensitive)
Function Test-IgnoreFile( $File, $Ignore ) {
foreach( $i in $Ignore ) {
if( $File.FullName.StartsWith( $i, [StringComparison]::OrdinalIgnoreCase ) ) {
return $true
}
}
$false
}
$limit = (Get-Date).AddDays(-30)
$del30 = "D:\CompanyX_ftp\users"
$ignore = Get-Content "C:\Users\UserX\Documents\Scripts\ignorelist.txt"
Get-ChildItem $del30 -File -Recurse |
Where-Object { $_.CreationTime -lt $limit -and -not ( Test-IgnoreFile $_ $ignore ) } |
ForEach-Object {
if( $ReportOnly) {
$_ | Select-Object FullName, CreationTime
}
else {
$_ | Remove-Item -Force
}
}

Why exclude clause not working as expected.. powershell?

I have a powershell script designed to go through some backup files, archive the month end backup(if its the first of the month) and delete anything older than 9 days after that. I'm trying to tell it to exclude the archive folder but it seems to be ignoring that and I'm unsure why.. kind of a noob to powershell.
I've tried using -notlike $Exlcude+% I've tried -notmatch -NE .. I even tried notcontains even though I knew that wouldn't work.. I tried applying the $exclude to every portion of the code where that folder might be accessed and its still making a copy of it.
#If first of the month copy latest backups to Monthly Backup Folder - will copy full folder path
PARAM($BackupPath="\\SomeServer\sqltestbackups\",
$Exclude= "\\SomeServer\sqltestbackups\Archive")
#Grab a recursive list of all subfolders
$SubFolders = dir $BackupPath -Recurse | Where-Object {$_.PSIsContainer} | ForEach-Object -Process {$_.FullName}
#Iterate through the list of subfolders and grab the first file in each
$Year = (get-date).Year
$Month = (get-date).Month
$StartOfMonth = Get-Date -Year $Year -Month $Month -Day 1
$Today = (Get-Date).day
$Date = (GET-DATE).AddDays(-9)
$PrevMonth = (GET-DATE).year.ToString()+(Get-Culture).DateTimeFormat.GetMonthName((Get-Date).AddMonths(-1).month)
$Destination=$Exclude + "\" + $Prevmonth +"\"
IF (!(Test-Path -path $destination)) {New-Item $destination -Type Directory}
IF($Today -eq '5')
{
$Path = $BackupPath #Root path to look for files
$DestinationPath = $Destination #Remote destination for file copy
#Grab a recursive list of all subfolders
$SubFolders = dir $Path -Recurse | Where-Object {$_.PSIsContainer -and $_.DirectoryName -notmatch $Exclude} | ForEach-Object -Process {$_.FullName}
#Iterate through the list of subfolders and grab the first file in each
ForEach ($Folder in $SubFolders)
{
$FullFileName = dir $Folder | Where-Object {!$_.PSIsContainer -and $_.DirectoryName -notmatch $Exclude} | Sort-Object {$_.LastWriteTime} -Descending | Select-Object -First 1
#For every file grab it's location and output the robocopy command ready for use
ForEach ($File in $FullFileName)
{
$FilePath = $File.DirectoryName
$ArchFolder = $File.DirectoryName
$ArchFolder=$ArchFolder.Replace($BackupPath,"")+"\"
$FinalPath=$destinationPath+$ArchFolder
$FileName = $File.Name
robocopy $FilePath $FinalPath $FileName /A-:SH /R:6 /W:30 /Z
}
}
}
# Delete files older than 9 days that are not contained within the month end folder
Get-ChildItem $BackupPath -Recurse |
Where-Object { !($_.PSIsContainer) -and
$_.LastWriteTime -lt $Date -and
$_.Directory -notlike $Exclude+"%" } |
Remove-Item
The code works except for the copy month end portion.. in this portion it is including the archive folder and I end up with it copying the previous month.. the script is designed to put the files in archive/YM/FullPath of backup so what is happening is its going Archive/YM/ARchive/YM/FullPath even though I'm trying SO HARD to exclude this path from the folders.
Example of whats going wrong with the robocopy
Source : \SomeServer\sqltestbackups\ARCHIVE\2019March\SomeOtherServer\SQLBackups\SomeDatabase\master\
Dest : \SomeServer\sqltestbackups\Archive\2019March\ARCHIVE\2019March\SomeOtherServer\SQLBackups\SomeDatabase\master\
the type DirectoryInfodoes not have a property DirectoryName. Try property BaseName instead.
# dir, gci are aliases for Get-ChildItem
Get-ChildItem $Path -Recurse `
| Where-Object { $_.PSIsContainer -and $_.BaseName -notmatch $Exclude }
works.
To see which types are returned and which members there are type
Get-ChildItem .\ | Where-Object { $_.PSIsContainer } | ForEach-Object { $_.GetType() }
# and
Get-ChildItem .\ | Where-Object { $_.PSIsContainer } | Get-Member
But be careful: You will get a lot of output on directories with many files and subdirectories.
Also have a look at the answer How can I exclude multiple folders using Get-ChildItem -exclude? on stackoverflow. This answer comes with lots of elegant solutions for PowerShell from v1.0 to v5.0.
problem was
$Exclude= "\\SomeServer\sqltestbackups\Archive" I needed to double up the \s to be
$Exclude= "\\\\SomeServer\\sqltestbackups\\Archive"
after doing this the script worked fine.

Get contents of subfolders containing a string in their name

I want to get all files in subfolders, of the same root folder, that all contain the same string ("foo") in the name of the subfolder(s). Below gives me no error, and no output. I don't know what I'm missing.
Get-ChildItem $rootfolder | where {$_.Attributes -eq 'Directory' -and $_.BaseName -contains 'foo'}) | echo $file
Ultimately, I would like to not just echo their names, but move each file to a target folder.
Thank you.
Here is a solution that includes moving the child files of each folder to a new target folder:
$RootFolder = '.'
$TargetFolder = '.\Test'
Get-ChildItem $RootFolder | Where-Object {$_.PSIsContainer -and $_.BaseName -match 'foo'} |
ForEach-Object { Get-ChildItem $_.FullName |
ForEach-Object { Move-Item $_.FullName $TargetFolder -WhatIf } }
Remove -WhatIf when you are happy it's doing what it should be.
You might need to modify the Get-ChildItem $_.FullName part if you (for example) want to exclude sub-directories of the folders, or if you want to include child items in all subfolders of those paths, but not the folders themselves.
replace
Get-ChildItem $rootfolder | where {$_.Attributes -match 'Directory' -and $_.basename -Match 'foo'}) | echo $file
with
Get-ChildItem $rootfolder | where {($_.Attributes -eq 'Directory') -and ($_.basename -like '*foo*')} | Move-Item $targetPath
your request:
that all contain the same string ("foo")
you have to use the -like comparison operator. Also for exact match I would use -eq (case sensitive version is -ceq) instead of -match since its used for matching substrings and patterns.
workflow:
Gets all the files in directory, sending it through pipe to Where-Object cmdlet where you are filtering based on properties Attributes and Basename. When the filtering is done, its being sent to cmdlet Move-Item.
Adapt the first two vars to your environment.
$rootfolder = 'C:\Test'
$target = 'X:\path\to\whereever'
Get-ChildItem $rootfolder -Filter '*foo*' |
Where {$_.PSiscontainer} |
ForEach-Object {
"Processing folder: {0} " -f $_
Move $_\* -Destination $target
}

Powershell - Exclude folders in Get-ChildItem

How to exclude folders ? Now I hardcode the folder names but i want it to be more flexible.
foreach($file in Get-ChildItem $fileDirectory -Exclude folderA,folderb)
"How to exclude folders ?" , if you mean all folders :
get-childitem "$fileDirectory\\*" -file
but it works only for the first level of $fileDirectory .
This works recursevly :
Get-ChildItem "$fileDirectory\\*" -Recurse | ForEach-Object { if (!($_.PSIsContainer)) { $_}}
or
Get-ChildItem "$fileDirectory\\*" -Recurse | where { !$_.PSisContainer }
You can do this by using the pipeline and a Where-Object filter.
First of all, the idiomatic way to iterate over a group of files in PowerShell is to pipe Get-Childitem to Foreach-Object. So rewriting your command gets:
Get-ChildItem $fileDirectory | foreach {
$file = $_
...
}
The advantage of using the pipeline is that now you can insert other cmdlets in between. Specifically, we use Where-Object to filter the list of files. The filter will pass on a file only if it isn't contained in a given array.
$excludelist = 'folderA', 'folderB'
Get-Childitem $fileDirectory |
where { $excludeList -notcontains $_ } |
foreach {
$file = $_
...
}
If you're going to use this a lot, you can even write a custom filter function to modify the list of files in an arbitrary way before passing to foreach.
filter except($except, $unless = #()) {
if ($except -notcontains $_ -or $unless -contains $_ ){
$_
}
}
$excludelist = 'folderA', 'folderB'
$alwaysInclude = 'folderC', 'folderD'
Get-ChildItem $fileDirectory |
except $excludeList -unless $alwaysInclude |
foreach {
...
}
#dvjz said that -file works only in the first level of a folder, but not recursively. But it seems to work for me.
get-childitem "$fileDirectory\\*" -file -recurse
For future googlers, I have found that files have a property called PSIsContainer which is $true when they are a directory.
A command listing all files in $fileDirectory would be:
foreach ($file in Get-ChildItem $fileDirectory | Where-Object -Property PSIsContainer -eq $false)
{
Write-Host $file.Name
}
Note that -Property is optional for the cmdlet Where-Object.
The simplest way to exclude your folders recursively:
foreach($file in Get-ChildItem $fileDirectory -Exclude {Get-ChildItem folderA},{Get-ChildItem folderB})
Where:
$fileDirectory - search folder
folderA, folderB - excluded folders

How to find files whose paths do not exist in the text file using powershell

I need to check and return files that exist in the filesystem, but are not listed in a given text file. For instance, my text file (sample.txt) will contain paths like:
\\SharedDrive\Data\DevS\Common\app_name\subfolder1\Archive\Archive1.vbproj
\\SharedDrive\Data\DevS\NotCommon\app_name\subfolder1\user\WebApp.vbproj
\\SharedDrive\Data\DevS\UnCommon\app_name\subfolder1\Manager\Managerial.vbproj
It happens that there are VB project files that exists on the drive but are not among the list, which i want to return along with their full path. For instance:
\\SharedDrive\Data\DevS\Common\app_name\subfolder2\Windows\SharedArchive.vbproj
\\SharedDrive\Data\DevS\NotCommon\app_name2\subfolder1\user2\WebApp2.vbproj
I tried this:
$log = "e:\pshell\notExists.log"
Get-Content "e:\pshell\Sample.txt" | Where-Object {
#Keep only paths that does not exists
!(Test-Path $_)
} | Set-Content $log
but this does the other way around.
Try this:
$baseDir = "\\SharedDrive\Data\DevS"
$log = "..."
$paths = Get-Content sample.txt
Get-ChildItem $baseDir -Recurse -Filter *.vbproj | ? {
-not $_.PSIsContainer -and $paths -notcontains $_.FullName
} | % { $_.FullName } | Set-Content $log