Powershell - Copy Script Loop - powershell

I have the below script:
function copyUserSettings {
Write-Host
$copyFrom = Read-Host 'Which Folders Do You Want To Copy FROM?'
Write-Host
$copyTo = Read-Host 'Which Folders Do You Want To Copy TO? (Enter a Number OR Range eg. 12-18)'
Write-Host
IF ($copyTo.Contains("-")) {
$copyToStart = $copyTo.Split("-")[0]
$copyToEnd = $copyTo.Split("-")[1]
$copyToStart..$copyToEnd | foreach{
Copy-Item -Path $rootPath\FOLDER\$copyFrom\US*.DAT -Destination $rootPath\FOLDER\$_
}
} else {
Copy-Item -Path $rootPath\FOLDER\$copyFrom\US*.DAT -Destination $rootPath\FOLDER\$copyTo
}
}
The user is supposed to enter where to copy the files from (all the folder names are just a number), and where to copy the files to (also just a number), by entering a single folder name or a range (ie 12-18). If I enter a single number the above script works properly, but if I enter a range the files don't copy and I don't get any feedback error or anything.
Edit1: $rootPath is defined earlier in the script.
Edit2: Modified code above per #tnw's suggestion.
Thanks in advance for your help. If you need any more details please let me know.

It appears you've misplaced a closing bracket. I've moved that bracket to where it should be (I think). You should exercise better tabbing to avoid these issues:
function copyUserSettings {
Write-Host
$copyFrom = Read-Host 'Which Folders Do You Want To Copy FROM?'
Write-Host
$copyTo = Read-Host 'Which Folders Do You Want To Copy TO? (Enter a Number OR Range eg. 12-18)'
Write-Host
IF ($copyTo -Contains "-") {
$copyToStart = $copyTo.Split("-")[0]
$copyToEnd = $copyTo.Split("-")[1]
$copyToStart..$copyToEnd | foreach {
Copy-Item -Path $rootPath\FOLDER\$copyFrom\US*.DAT -Destination $rootPath\FOLDER\$_
} #this bracket was missing
} else {
Copy-Item -Path $rootPath\FOLDER\$copyFrom\US*.DAT -Destination $rootPath\FOLDER\$copyTo
}
} #you had an extra closing bracket here
EDIT: Figured it out. Look at this:
"12-18" -Contains "-"
Evaluates to false, so your if never evaluates to true. This is because -Contains is for collections, not substrings. From the documentation: "Tells whether a collection of reference values includes a single test value"
Try this instead:
IF ($copyTo.Contains("-"))
You can also use the -Match operator:
IF ($copyTo -Match "-")

The -contains operator checks if a set (e.g. a list/array) contains a particular item. To check if a string contains a particular character you need to use the -like or -match operator:
$copyTo -like '*-*'
$copyTo -match '-'
The -match operator would also allow you to verify if the string contains numbers in the right places and extract those numbers from the string:
if ($copyTo -match '^(\d+)-(\d+)$') {
$start = $matches[1]
$end = $matches[2]
}
Another option would be using the Contains() method of the string class as tnw suggested:
$copyTo.Contains('-')

If I understand your question correctly, $_ is supposed to be destination folder name. But your script doesn't know that so it treats $_ as file name. You need to create destination folders (if they don't exist) and change your Copy-Item destination. Example:
$copyToStart..$copyToEnd | foreach {
New-Item -Type Directory -Path $rootPath\FOLDER\$_
Copy-Item -Path $rootPath\FOLDER\$copyFrom\US*.DAT -Destination $rootPath\FOLDER\$_\US*.DAT
}

Related

How to pipe Rename-Item into Move-Item (powershell)

I'm in the process of writing up a PowerShell script that can take a bunch of .TIF images, rename them, and place them in a new folder structure depending on the original file name.
For example, a folder containing the file named:
ABC-ALL-210316-0001-3001-0001-1-CheckInvoice-Front.TIF
would be renamed to "00011CIF.TIF", and placed in the following folder:
\20220316\03163001\
I've been trying to put together a code to perform this task, and I got one to work where I had two different "ForEach" methods. One would do a bunch of file renaming to remove "-" and shorten "CheckInvoiceFront" to "CIF" and such. Then the second method would again pull all .TIF images, create substrings of the image names, and create folders from those substrings, and then move the image to the new folder, shortening the file name. Like I said, it worked... but I wanted to combine the ForEach methods into one process. However, each time I try to run it, it fails for various reasons... I've tried to change things around, but I just can't seem to get it to work.
Here's the current (non-working) code:
# Prompt user for directory to search through
$sorDirectory = Read-Host -Prompt 'Input source directory to search for images: '
$desDirectory = Read-Host -Prompt 'Input target directory to output folders: '
Set-Location $sorDirectory
# Check directory for TIF images, and close if none are found
Write-Host "Scanning "$sorDirectory" for images... "
$imageCheck = Get-ChildItem -File -Recurse -Path $sorDirectory -include '*.tif'
$imageCount = $imageCheck.count
if ($imageCount -gt 0) {
Write-Host "Total number of images found: $imageCount"
""
Read-Host -Prompt "Press ENTER to continue or CTRL+C to quit"
$count1=1;
# Rename all images, removing "ABCALL" from the start and inserting "20", and then shorten long filetype names, and move files to new folders with new names
Clear-Host
Write-Host "Reformatting images for processing..."
""
Get-ChildItem -File -Recurse -Path $sorDirectory -include '*.tif' |
ForEach-Object {
Write-Progress -Activity "Total Formatted Images: $count1/$imageCount" -Status "0--------10%--------20%--------30%--------40%--------50%--------60%--------70%--------80%--------90%-------100" -CurrentOperation $_ -PercentComplete (($count1 / $imageCount) * 100)
Rename-Item $_ -NewName $_.Name.Replace("-", "").Replace("ABCALL", "20").Replace("CheckInvoiceFront", "CIF").Replace("CheckInvoiceBack", "CIB").Replace("CheckFront", "CF").Replace("CheckBack", "CB") |Out-Null
$year = $_.Name.SubString(0, 4)
$monthday = $_.Name.Substring(4,4)
$batch = $_.Name.SubString(12, 4)
$fulldate = $year+$monthday
$datebatch = $monthday+$batch
$image = $_.Name.SubString(16)
$fullPath = "$desDirectory\$fulldate\$datebatch"
if (-not (Test-Path $fullPath)) { mkdir $fullPath |Out-Null }
Move-Item $_ -Destination "$fullPath\$image" |Out-Null
$count1++
}
# Finished
Clear-Host
Write-Host "Job complete!"
Timeout /T -1
}
# Closes if no images are found (likely bad path)
else {
Write-Host "There were no images in the selected folder. Now closing..."
Timeout /T 10
Exit
}
Usually this results in an error stating that it's can't find the path of the original file name, as if it's still looking for the original non-renamed image. I tried adding some other things, but then it said I was passing null values. I'm just not sure what I'm doing wrong.
Note that if I take the everything after the "Rename-Item" (starting with "$year =") and have that in a different ForEach method, it works. I guess I just don't know how to make the Rename-Item return its results back to "$_" before everything else tries working on it. I tried messing around with "-PassThru" but I don't think I was doing it right.
Any suggestions?
As Olaf points out, situationally you may not need both a Rename-Item and a Move-Item call, because Move-Item can rename and move in single operation.
That said, Move-Item does not support implicit creation of the target directory to move a file to, so in your case you do need separate calls.
You can use Rename-Item's -PassThru switch to make it output a System.IO.FileInfo instance (or, if a directory is being renamed, a System.IO.DirectoryInfo instance) representing the already renamed file; you can directly pass such an instance to Move-Item via the pipeline:
Get-ChildItem -File -Recurse -Path $sorDirectory -include '*.tif' |
ForEach-Object {
# ...
# Use -PassThru with Rename-Item to output a file-info object describing
# the already renamed file.
$renamedFile = $_ | Rename-Item -PassThru -NewName $_.Name.Replace("-", "").Replace("ABCALL", "20").Replace("CheckInvoiceFront", "CIF").Replace("CheckInvoiceBack", "CIB").Replace("CheckFront", "CF").Replace("CheckBack", "CB")
# ...
# Pass $renamedFile to Move-Item via the pipeline.
$renamedFile | Move-Item -Destination "$fullPath\$image"
# ...
}
As for your desire to:
make the Rename-Item return its results back to "$_"
While PowerShell doesn't prevent you from modifying the automatic $_ variable, it is better to treat automatic variables as read-only.
Therefore, a custom variable is used above to store the output from Rename-Item -PassThru
You need -passthru and -destination:
rename-item file1 file2 -PassThru | move-item -Destination dir1

Script lists all files that don't contain needed content

I'm trying to find all files in a dir, modified within the last 4 hours, that contain a string. I can't have the output show files that don't contain needed content. How do I change this so it only lists the filename and content found that matches the string, but not files that don't have that string? This is run as a windows shell command. The dir has a growing list of hundreds of files, and currently output looks like this:
File1.txt
File2.txt
File3.txt
... long long list, with none containing the needed string
(powershell "Set-Location -Path "E:\SDKLogs\Logs"; Get-Item *.* | Foreach { $lastupdatetime=$_.LastWriteTime; $nowtime = get-date; if (($nowtime - $lastupdatetime).totalhours -le 4) {Select-String -Path $_.Name -Pattern "'Found = 60.'"| Write-Host "$_.Name Found = 60"; }}")
I tried changing the location of the Write-Host but it's still printing all files.
Update:
I'm currently working on this fix. Hopefully it's what people were alluding to in comments.
$updateTimeRange=(get-date).addhours(-4)
$fileNames = Get-ChildItem -Path "K:\NotFound" -Recurse -Include *.*
foreach ($file in $filenames)
{
#$content = Get-Content $_.FullName
Write-host "$($file.LastWriteTime)"
if($file.LastWriteTime -ge $($updateTimeRange))
{
#Write-Host $file.FullName
if(Select-String -Path $file.FullName -Pattern 'Thread = 60')
{
Write-Host $file.FullName
}
}
}
If I understood you correctly, you just want to display the file name and the matched content? If so, the following will work for you:
$date = (Get-Date).AddHours(-4)
Get-ChildItem -Path 'E:\SDKLogs\Logs' | Where-Object -FilterScript { $date -lt $_.LastWriteTime } |
Select-String -Pattern 'Found = 60.' |
ForEach-Object -Process {
'{0} {1}' -f $_.FileName, $_.Matches.Value
}
Get-Date doesn't need to be in a variable before your call but, it can become computationally expensive running a call to it again and again. Rather, just place it in a variable before your expression and call on the already created value of $date.
Typically, and for best practice, you always want to filter as far left as possible in your command. In this case we swap your if statement for a Where-Object to filter as the objects are passed down the pipeline. Luckily for us, Select-String returns the file name of a match found, and the matched content so we just reference it in our Foreach-Object loop; could also use a calculated property instead.
As for your quoting issues, you may have to double quote or escape the quotes within the PowerShell.exe call for it to run properly.
Edit: swapped the double quotes for single quotes so you can wrap the entire expression in just PowerShell.exe -Command "expression here" without the need of escaping; this works if you're pattern to find doesn't contain single quotes.

How to copy multiple value if we passed multiple value

I am using the below code to copy the folder but when I enter a1,a2 then it's not working, only work with single platforms.
$publish="C:\Publish"
$finalpublish="C:\Publish\Final_Publish"
Write-host "a1,a2,a3"
$platformvalue= Read-Host "Please enter Platform name which have dll in release, Please find above list for your reference"
IF($platformvalue -eq 'a1'){
Copy-Item $publish\a1 -Destination $finalpublish\a1 -Recurse
}
IF($platformvalue -eq 'a2'){
Copy-Item $publish\a2 -Destination $finalpublish\a2 -Recurse
}
IF($platformvalue -eq 'a3'){
Copy-Item $publish\a3 -Destination $finalpublish\a3 -Recurse
}
Read-Host only allows input as a simple string, but you seem to expect it to behave like an array with multiple values. So you either need to set up your script properly with parameters that you can pass your input to (this is a simple introduction), or parse your Read-Host string first so that you can process each item.
For exmple, if you are putting commas between each item, you can split it into an array. You can quickly see how it works in the console.
> $platformvalue = (Read-host "prompt") -split ','
prompt: a1,a2,a3
# the input is now separate items if you output them in a Foreach-Item loop
> Foreach ($i in $platformvalue) { write-host "Output $i" }
Output a1
Output a2
Output a3
Once $platformvalue has been split into one or many items, you need to process them with Foreach-Item (that isn't strictly true if there's only one item, but you'd need more code to check - waste of time).
Foreach ($i in $platformvalue) {
IF($i -eq 'a1'){
Copy-Item $publish\a1 -Destination $finalpublish\a1 -Recurse
}
...
}
By the way, if your variable will contain the entire string needed to accurately construct each path, you don't need the If statements.
For example, if $platformvalue is an array like #("a1","a2") (from splitting the Read-Host or whatever), and those strings are sufficent to construct your paths, you only need one copy statement:
Foreach ($i in ("a1","a2")) {
Copy-Item $publish\$i -Destination $finalpublish\$i
# 1st loop: $i = 'a1'
# Result: $publish\a1 -Destination $finalpublish\a1
# 2nd loop: $i = 'a2'
# Result: $publish\a2 -Destination $finalpublish\a2
}
You'd only need all those Ifs if you're doing other modifications to the paths. If that's the case, I'd highly recommend using a Switch statement instead of a chain of Ifs.

Renaming script in PowerShell

I'm trying to write a script to remove characters from file names in a certain folder using PowerShell. It prompts the user to enter how many characters they want removed from the front, back, and a certain string they want removed.
# Prompt User how many characters in the front they want removed
$FrontRemove = Read-Host 'Enter how many characters you want removed from the front'
# Prompt user how many characters in the back they want removed
$BackRemove = Read-Host 'Enter how many characters you want removed from the back'
# Prompt user for string to be removed
$MiddleRemove = Read-Host 'Enter a string you want removed from the file name'
dir | Rename-Item -NewName{$_.name.substring(0,$_.BaseName.length-$BackRemove)}
dir | Rename-Item -NewName{$_.name.substring($FrontRemove)}
dir | Rename-Item -NewName{$_.name -replace "$MiddleRemove", ""}
The current issue I'm having is that it's removing the extensions of these files and it's also renaming the script itself. How would I go about keeping file extensions and excluding .ps1?
There are a few things I would change in your code. For one thing, you call dir (Get-ChildItem) for every change of the file names when you could do that in one call.
Also, it lacks any form of checking to see if what the user has entered can actually be done for each file the Get-ChildItem returns.
The Get-ChildItem without a path specified will search items in the current location. If that is not what you want, maybe it is safer to set the path like in the code below.
$folder = '<ENTER THE PATH TO THE FOLDER WHERE THE FILES TO RENAME ARE HERE>'
# Prompt User how many characters in the front they want removed --> number
[int]$FrontRemove = Read-Host 'Enter how many characters you want removed from the front'
# Prompt user how many characters in the back they want removed --> number
[int]$BackRemove = Read-Host 'Enter how many characters you want removed from the back'
# Prompt user for string to be removed --> string
[string]$MiddleRemove = Read-Host 'Enter a string you want removed from the file name'
# Since we are using the -replace function, which is using Regular Expression replacement,
# we need to make sure all 'special' characters in the string are escaped.
if (![string]::IsNullOrEmpty($MiddleRemove)) {
$MiddleRemove = [Regex]::Escape($MiddleRemove)
}
Get-ChildItem -Path $folder -File | Where-Object {$_.Name -notlike '*.ps1'} |
ForEach-Object {
$directory = $_.DirectoryName # or [System.IO.Path]::GetDirectoryName($_.FullName) or use Split-Path $_.FullName -Parent
$filename = $_.BaseName # or [System.IO.Path]::GetFileNameWithoutExtension($_.Name)
$extension = $_.Extension # or [System.IO.Path]::GetExtension($_.Name)
# test user input and remove/replace only if possible
if ($FrontRemove -gt 0 -and $FrontRemove -lt $filename.Length) {
$filename = $filename.Substring($FrontRemove)
}
if ($BackRemove -gt 0 -and $BackRemove -lt $filename.Length) {
$filename = $filename.Substring(0, $filename.Length - $BackRemove)
}
if (![string]::IsNullOrEmpty($MiddleRemove)) {
$filename = $filename -replace $MiddleRemove, ''
}
# now see if we still have a name left and if indeed the filename has changed
if (![string]::IsNullOrEmpty($filename) -and $filename -ne $_.BaseName) {
# re-append the extension of the file
if (![string]::IsNullOrEmpty($extension)) { $filename += $extension }
# join it with the directory to become a complete path and filename
$newname = Join-Path -Path $directory -ChildPath $filename
Rename-Item -LiteralPath $_.FullName -NewName $newname -Force
}
else {
Write-Warning "The options you entered would remove the entire filename. Action skipped on '$($_.FullName)'"
}
}

How to retrieve a recursive directory and file list from PowerShell excluding some files and folders?

I want to write a PowerShell script that will recursively search a directory, but exclude specified files (for example, *.log, and myFile.txt), and also exclude specified directories, and their contents (for example, myDir and all files and folders below myDir).
I have been working with the Get-ChildItem CmdLet, and the Where-Object CmdLet, but I cannot seem to get this exact behavior.
I like Keith Hill's answer except it has a bug that prevents it from recursing past two levels. These commands manifest the bug:
New-Item level1/level2/level3/level4/foobar.txt -Force -ItemType file
cd level1
GetFiles . xyz | % { $_.fullname }
With Hill's original code you get this:
...\level1\level2
...\level1\level2\level3
Here is a corrected, and slightly refactored, version:
function GetFiles($path = $pwd, [string[]]$exclude)
{
foreach ($item in Get-ChildItem $path)
{
if ($exclude | Where {$item -like $_}) { continue }
$item
if (Test-Path $item.FullName -PathType Container)
{
GetFiles $item.FullName $exclude
}
}
}
With that bug fix in place you get this corrected output:
...\level1\level2
...\level1\level2\level3
...\level1\level2\level3\level4
...\level1\level2\level3\level4\foobar.txt
I also like ajk's answer for conciseness though, as he points out, it is less efficient. The reason it is less efficient, by the way, is because Hill's algorithm stops traversing a subtree when it finds a prune target while ajk's continues. But ajk's answer also suffers from a flaw, one I call the ancestor trap. Consider a path such as this that includes the same path component (i.e. subdir2) twice:
\usr\testdir\subdir2\child\grandchild\subdir2\doc
Set your location somewhere in between, e.g. cd \usr\testdir\subdir2\child, then run ajk's algorithm to filter out the lower subdir2 and you will get no output at all, i.e. it filters out everything because of the presence of subdir2 higher in the path. This is a corner case, though, and not likely to be hit often, so I would not rule out ajk's solution due to this one issue.
Nonetheless, I offer here a third alternative, one that does not have either of the above two bugs. Here is the basic algorithm, complete with a convenience definition for the path or paths to prune--you need only modify $excludeList to your own set of targets to use it:
$excludeList = #("stuff","bin","obj*")
Get-ChildItem -Recurse | % {
$pathParts = $_.FullName.substring($pwd.path.Length + 1).split("\");
if ( ! ($excludeList | where { $pathParts -like $_ } ) ) { $_ }
}
My algorithm is reasonably concise but, like ajk's, it is less efficient than Hill's (for the same reason: it does not stop traversing subtrees at prune targets). However, my code has an important advantage over Hill's--it can pipeline! It is therefore amenable to fit into a filter chain to make a custom version of Get-ChildItem while Hill's recursive algorithm, through no fault of its own, cannot. ajk's algorithm can be adapted to pipeline use as well, but specifying the item or items to exclude is not as clean, being embedded in a regular expression rather than a simple list of items that I have used.
I have packaged my tree pruning code into an enhanced version of Get-ChildItem. Aside from my rather unimaginative name--Get-EnhancedChildItem--I am excited about it and have included it in my open source Powershell library. It includes several other new capabilities besides tree pruning. Furthermore, the code is designed to be extensible: if you want to add a new filtering capability, it is straightforward to do. Essentially, Get-ChildItem is called first, and pipelined into each successive filter that you activate via command parameters. Thus something like this...
Get-EnhancedChildItem –Recurse –Force –Svn
–Exclude *.txt –ExcludeTree doc*,man -FullName -Verbose
... is converted internally into this:
Get-ChildItem | FilterExcludeTree | FilterSvn | FilterFullName
Each filter must conform to certain rules: accepting FileInfo and DirectoryInfo objects as inputs, generating the same as outputs, and using stdin and stdout so it may be inserted in a pipeline. Here is the same code refactored to fit these rules:
filter FilterExcludeTree()
{
$target = $_
Coalesce-Args $Path "." | % {
$canonicalPath = (Get-Item $_).FullName
if ($target.FullName.StartsWith($canonicalPath)) {
$pathParts = $target.FullName.substring($canonicalPath.Length + 1).split("\");
if ( ! ($excludeList | where { $pathParts -like $_ } ) ) { $target }
}
}
}
The only additional piece here is the Coalesce-Args function (found in this post by Keith Dahlby), which merely sends the current directory down the pipe in the event that the invocation did not specify any paths.
Because this answer is getting somewhat lengthy, rather than go into further detail about this filter, I refer the interested reader to my recently published article on Simple-Talk.com entitled Practical PowerShell: Pruning File Trees and Extending Cmdlets where I discuss Get-EnhancedChildItem at even greater length. One last thing I will mention, though, is another function in my open source library, New-FileTree, that lets you generate a dummy file tree for testing purposes so you can exercise any of the above algorithms. And when you are experimenting with any of these, I recommend piping to % { $_.fullname } as I did in the very first code fragment for more useful output to examine.
The Get-ChildItem cmdlet has an -Exclude parameter that is tempting to use but it doesn't work for filtering out entire directories from what I can tell. Try something like this:
function GetFiles($path = $pwd, [string[]]$exclude)
{
foreach ($item in Get-ChildItem $path)
{
if ($exclude | Where {$item -like $_}) { continue }
if (Test-Path $item.FullName -PathType Container)
{
$item
GetFiles $item.FullName $exclude
}
else
{
$item
}
}
}
Here's another option, which is less efficient but more concise. It's how I generally handle this sort of problem:
Get-ChildItem -Recurse .\targetdir -Exclude *.log |
Where-Object { $_.FullName -notmatch '\\excludedir($|\\)' }
The \\excludedir($|\\)' expression allows you to exclude the directory and its contents at the same time.
Update: Please check the excellent answer from msorens for an edge case flaw with this approach, and a much more fleshed out solution overall.
Recently, I explored the possibilities to parameterize the folder to scan through and the place where the result of recursive scan will be stored. At the end, I also did summarize the number of folders scanned and number of files inside as well. Sharing it with community in case it may help other developers.
##Script Starts
#read folder to scan and file location to be placed
$whichFolder = Read-Host -Prompt 'Which folder to Scan?'
$whereToPlaceReport = Read-Host -Prompt 'Where to place Report'
$totalFolders = 1
$totalFiles = 0
Write-Host "Process started..."
#IMP separator ? : used as a file in window cannot contain this special character in the file name
#Get Foldernames into Variable for ForEach Loop
$DFSFolders = get-childitem -path $whichFolder | where-object {$_.Psiscontainer -eq "True"} |select-object name ,fullName
#Below Logic for Main Folder
$mainFiles = get-childitem -path "C:\Users\User\Desktop" -file
("Folder Path" + "?" + "Folder Name" + "?" + "File Name " + "?"+ "File Length" )| out-file "$whereToPlaceReport\Report.csv" -Append
#Loop through folders in main Directory
foreach($file in $mainFiles)
{
$totalFiles = $totalFiles + 1
("C:\Users\User\Desktop" + "?" + "Main Folder" + "?"+ $file.name + "?" + $file.length ) | out-file "$whereToPlaceReport\Report.csv" -Append
}
foreach ($DFSfolder in $DFSfolders)
{
#write the folder name in begining
$totalFolders = $totalFolders + 1
write-host " Reading folder C:\Users\User\Desktop\$($DFSfolder.name)"
#$DFSfolder.fullName | out-file "C:\Users\User\Desktop\PoC powershell\ok2.csv" -Append
#For Each Folder obtain objects in a specified directory, recurse then filter for .sft file type, obtain the filename, then group, sort and eventually show the file name and total incidences of it.
$files = get-childitem -path "$whichFolder\$($DFSfolder.name)" -recurse
foreach($file in $files)
{
$totalFiles = $totalFiles + 1
($DFSfolder.fullName + "?" + $DFSfolder.name + "?"+ $file.name + "?" + $file.length ) | out-file "$whereToPlaceReport\Report.csv" -Append
}
}
# If running in the console, wait for input before closing.
if ($Host.Name -eq "ConsoleHost")
{
Write-Host ""
Write-Host ""
Write-Host ""
Write-Host " **Summary**" -ForegroundColor Red
Write-Host " ------------" -ForegroundColor Red
Write-Host " Total Folders Scanned = $totalFolders " -ForegroundColor Green
Write-Host " Total Files Scanned = $totalFiles " -ForegroundColor Green
Write-Host ""
Write-Host ""
Write-Host "I have done my Job,Press any key to exit" -ForegroundColor white
$Host.UI.RawUI.FlushInputBuffer() # Make sure buffered input doesn't "press a key" and skip the ReadKey().
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}
##Output
##Bat Code to run above powershell command
#ECHO OFF
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%MyPowerShellScript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%PowerShellScriptPath%""' -Verb RunAs}";
A bit late, but try this one.
function Set-Files($Path) {
if(Test-Path $Path -PathType Leaf) {
# Do any logic on file
Write-Host $Path
return
}
if(Test-Path $path -PathType Container) {
# Do any logic on folder use exclude on get-childitem
# cycle again
Get-ChildItem -Path $path | foreach { Set-Files -Path $_.FullName }
}
}
# call
Set-Files -Path 'D:\myFolder'
Commenting here as this seems to be the most popular answer on the subject for searching for files whilst excluding certain directories in powershell.
To avoid issues with post filtering of results (i.e. avoiding permission issues etc), I only needed to filter out top level directories and that is all this example is based on, so whilst this example doesn't filter child directory names, it could very easily be made recursive to support this, if you were so inclined.
Quick breakdown of how the snippet works
$folders << Uses Get-Childitem to query the file system and perform folder exclusion
$file << The pattern of the file I am looking for
foreach << Iterates the $folders variable performing a recursive search using the Get-Childitem command
$folders = Get-ChildItem -Path C:\ -Directory -Name -Exclude Folder1,"Folder 2"
$file = "*filenametosearchfor*.extension"
foreach ($folder in $folders) {
Get-Childitem -Path "C:/$folder" -Recurse -Filter $file | ForEach-Object { Write-Output $_.FullName }
}