Delete files defined in an array with Powershell - powershell

Is it possible to define an array of filenames (all files in different folders) and then in a loop delete them all, or do something else?
Actually I need to create a few symbolic links using mklink to one file, putting those links in a different folders, replacing the old links if there was any.

Deleting an array of filenames is simple:
Remove-Item foo.txt,c:\temp\bar.txt,baz\baz.txt
Or via a variable:
$files = 'foo.txt','c:\temp\bar.txt','baz\baz.txt'
Remove-Item $files
And then based on all files in different folders:
$folders = 'C:\temp','C:\users\joe\foo'
Get-ChildItem $folders -r | Where {!$_.PSIsContainer} | Remove-Item -WhatIf
Remove the -WhatIf to do the actual removal.
If you want to delete a file with a specific name you could use the -Filter parameter on Get-ChildItem. This would be the best performing approach:
$folders = 'C:\temp','C:\users\joe\foo'
Get-ChildItem $folders -r -filter foo.bak | Remove-Item -WhatIf
If the name requires more sophisticated matching then you can use a regex in a Where scriptblock e.g.:
$folders = 'C:\temp','C:\users\joe\foo'
Get-ChildItem $folders -r | Where {$_.Name -match 'f\d+.bak$'} |
Remove-Item -WhatIf

something like this should work:
can't test right now, sorry
$filenames = #('filename1.txt', 'filename2.txt', 'filename3.txt')
foreach($file in $filenames)
{
#GCI recursive to find all instances of this filename
$filesToDelete = Get-ChildItem -R | where {$_.Name -eq $file}
foreach($f in $filesToDelete)
{
#delete the file that matches, etc. here
# just using Write-Host to echo out the name for now
Write-Host $f.Name
}
}
As with most powershell, you can really compress this, but wanted to extend for explanation.
You could extend this to match your needs. For example if you needed all files that contain the word "delete", you could do gci | where {$_.Name -like "$file"}

Related

Rename files in subfolders

I am trying to rename files in subfolders in a certain pattern, but I am stuck.
The situation is as follows: I have multiple folders which are sometimes named as the target filename depending on the length, but the name does not really matter.
In each folder are always 2 files: the Target-File with a random name and the correct extension, and the Source-File which is always the correct BaseName with a txt-extension.
For example:
Folder1\7393028473.docx
Folder1\January.txt
Folder2\9373930843.pdf
Folder2\February.txt
My goal is to rename every not-txt-file with the Basename of the txt-file. Executed, it should be like:
Folder1\January.docx
Folder1\January.txt
Folder2\February.pdf
Folder2\February.txt
With gci I was able to create both lists but didn't find a good way for the renaming.
$SourceName = gci -File -Recurse | Where {$_.Extension -ne ".txt"}
$TargetName = gci -File -Recurse | Where {$_.Extension -eq ".txt"}
I did also try to use gci for renaming, but was not able to tell it to use the newname based on the txt-file:
gci -File -Recurse | Where {$_.Extension -ne ".txt"} | Rename-Item -NewName {$_.extension -eq ".txt"}
This only renamed the .docx-file to "FALSE" because the filename already exists.
What I did not try (but would be ok) is to not only rename the file, but also move it to the parent directory.
This is one way to do it but it would fail as soon as there are 2 or more files with a different extension than .txt but having the same extension. It would also fail as soon as one folder has more than one .txt file.
# Get all folders under 'TargetDirectory'
Get-ChildItem TargetDirectory -Directory -Recurse | ForEach-Object {
# For each sub-folder, get their files
$childs = $_.EnumerateFiles()
# Filter and split the child files by their extension
$txt, $notTxt = $childs.Where({ $_.Extension -eq '.txt' }, 'Split')
# Use the BaseName of the '.txt' File but the Extension of
# the file being renamed
$notTxt | Rename-Item -NewName { $txt.BaseName + $_.Extension }
}
Thanks for your reply and sorry for my late reply.
I tried your code but its not working correctly:
The NewName is created correctly, but the problem is the rename-function or rather the notTxt list because it only contains the item itself but not hte full path.
When I copy the file which should be renamed into the parent-directory your code does work in the file in the parent-directory.
There was another answer which apperntly was deleted but did work.
I also tried a foreach-loop in one of my tries but didn't get the NewName to work.
I don't know why, but I didn't consider creating the NewName with a variable, which was done in the deleted answer:
$folders = gci -Directory -Recurse
foreach ($folder in $folders) {
$targetFile = gci $folder | Where {$_.Extension -ne ".txt"}
$sourceFile = gci $folder | Where {$_.Extension -eq ".txt"}
$newName = $sourceFile.BaseName + $targetFile.Extension
Rename-Item $targetFile.FullName $newName
}
Of course you can try and get your code to work, but I can make do with this code.
Thank you very much for your help.

get-childitem find files in folders with same names

I need to check that all files in folders with the naming ".policy" is ether 'azureDeploy.parameters.json' or 'azureDeploy.json'
if i only want it to check one folder i just give the full path as here:
$azureDeployFiles = #('azureDeploy.parameters.json', 'azureDeploy.json')
(Get-ChildItem -path ..\..\fes\.policy -file -Recurse).name | Should -BeIn $azureDeployFiles
and that works. But i have multiple folders called something like '.\..\..\.policy' and i would like to check if all the folders with that naming only contains files with the naming 'azureDeploy.parameters.json' or 'azureDeploy.json'
You could specify 'only files in folders named X' with Where-Object on the Directory property:
$azureDeployFiles = #('azureDeploy.parameters.json', 'azureDeploy.json')
Get-ChildItem -path ..\..\ -Recurse -File |
Where Directory -Like "*\.policy" |
# example checking file names with -In
Foreach { $_.Name -In $azureDeployFiles }
Continuing from my comment. . .with a little help from the pipeline, we can use Where-Object to filter for directories with that specific name:
$azureDeployFiles = #('azureDeploy.parameters.json', 'azureDeploy.json')
Get-ChildItem -Path '.\MyPath' -Include $azureDeployFiles -Recurse |
Where-Object Directory -Like '*\.Policy'

File Sorting Based on Similar File and Folder Names

Im still generally new to powershell, and I am trying to create a program that will take files based on their name, and move them into folders that have a similar name but not exactly the same.
For example, Lets say I have 3 files, Apples.txt, Grapes.txt, and Oranges.txt. And I want to move them into corresponding folders, ApplesUSA, GrapesNY, OrangesFL.
I could just hard code it using a loop and a If-Then Statement. i.e If Apples.txt exists move to ApplesUSA. But I want it to be dynamic, so if other files and folders are added later I dont have to update the code. Is there a way to write a statement that would say if FileA and FolderB are similar in name (both contain apples in the name somewhere) then move fileA to FolderB and so on.
Any help appreciated. Thanks!!!!
try Something like this
$PathWithFile="C:\temp\Test"
$PathWithDir="C:\temp\Test"
Get-ChildItem $PathWithFile -file -Filter "*.txt" | %{
$CurrentFile=$_
$Dirfounded=Get-ChildItem $PathWithDir -Directory | where {$_ -match $CurrentFile.BaseName} | select FullName -First 1
if ($Dirfounded -ne $null)
{
move-Item $CurrentFile.FullName -Destination $Dirfounded.FullName -WhatIf
}
}
A oneliner similar to #Esperento's
gci *.txt -af|%{$File=$_.FullName;gci "$($_.BaseName)*" -ad|%{Move $File -Dest $($_.FullName) -whatif}}
The verbose version:
PushD "X:\path\to\base\folder"
Get-ChildItem *.txt -File | ForEach-Object{
$File = $_.FullName
Get-ChildItem "$($_.BaseName)*" -Directory | ForEach-Object {
Move-Item $File -Destination $_.FullName -whatif
}
}
PopD
Both versions require PowerShell V3 for the -File and -Directory parameters (and their aliases -af/-ad) This can be substituted by an additional |Where-Object{ $_.PSIsContainer} respective | Where-Object{!$_.PSIsContainer}

Recursively Delete Files and Directories Using a Filter on the Directory Name

I am attempting to delete all directories, sub-directories, and the files contained in them based on a filter that specifies the required directory/sub-directory name.
For example, if I have c:\Test\A\B.doc, c:\Test\B\A\C.doc, and c:\Test\B\A.doc and my filter specifies all directories named 'A', I would expect the remaining folders and files to be c:\Test, c:\Test\B and c:\Test\B\A.doc respectively.
I am trying to do this in PowerShell and am not familiar with it.
The following 2 examples will delete all of the files that match my specified filter, but the files that match the filter as well.
$source = "C:\Powershell_Test" #location of directory to search
$strings = #("A")
cd ($source);
Get-ChildItem -Include ($strings) -Recurse -Force | Remove-Item -Force –Recurse
and
Remove-Item -Path C:\Powershell_Test -Filter A
I would use something like this:
$source = 'C:\root\folder'
$names = #('A')
Get-ChildItem $source -Recurse -Force |
Where-Object { $_.PSIsContainer -and $names -contains $_.Name } |
Sort-Object FullName -Descending |
Remove-Item -Recurse -Force
The Where-Object clause restricts the output from Get-ChildItem to just folders whose names are present in the array $names. Sorting the remaining items by their full name in descending order ensures that child folders get deleted before their parent. That way you avoid errors from attempting to delete a folder that had already been deleted by a prior recursive delete operation.
If you have PowerShell v3 or newer you can do all filtering directly with Get-ChildItem:
Get-ChildItem $source -Directory -Include $names -Recurse -Force |
Sort-Object FullName -Descending |
Remove-Item -Recurse -Force
I don't think you can do it quite that simply. This gets the list of directories, and breaks the path into its constituent parts, and verifies whether the filter matches one of those parts. If so, it removes the whole path.
It adds a little caution to handle if it already deleted a directory because of nesting (the test-path) and the -Confirm helps ensure that if there's a bug here you have a chance to verify the behavior.
$source = "C:\Powershell_Test" #location of directory to search
$filter = "A"
Get-Childitem -Directory -Recurse $source |
Where-Object { $_.FullName.Split([IO.Path]::DirectorySeparatorChar).Contains($filter) } |
ForEach-Object { $_.FullName; if (Test-Path $_) { Remove-Item $_ -Recurse -Force -Confirm } }

How do I get only directories using Get-ChildItem?

I'm using PowerShell 2.0 and I want to pipe out all the subdirectories of a certain path. The following command outputs all files and directories, but I can't figure out how to filter out the files.
Get-ChildItem c:\mypath -Recurse
I've tried using $_.Attributes to get the attributes but then I don't know how to construct a literal instance of System.IO.FileAttributes to compare it to. In cmd.exe it would be
dir /b /ad /s
For PowerShell 3.0 and greater:
Get-ChildItem -Directory
You can also use the aliases dir, ls, and gci
For PowerShell versions less than 3.0:
The FileInfo object returned by Get-ChildItem has a "base" property, PSIsContainer. You want to select only those items.
Get-ChildItem -Recurse | ?{ $_.PSIsContainer }
If you want the raw string names of the directories, you can do
Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | Select-Object FullName
In PowerShell 3.0, it is simpler:
Get-ChildItem -Directory #List only directories
Get-ChildItem -File #List only files
Use
Get-ChildItem -dir #lists only directories
Get-ChildItem -file #lists only files
If you prefer aliases, use
ls -dir #lists only directories
ls -file #lists only files
or
dir -dir #lists only directories
dir -file #lists only files
To recurse subdirectories as well, add -r option.
ls -dir -r #lists only directories recursively
ls -file -r #lists only files recursively
Tested on PowerShell 4.0, PowerShell 5.0 (Windows 10), PowerShell Core 6.0 (Windows 10, Mac, and Linux), and PowerShell 7.0 (Windows 10, Mac, and Linux).
Note: On PowerShell Core, symlinks are not followed when you specify the -r switch. To follow symlinks, specify the -FollowSymlink switch with -r.
Note 2: PowerShell is now cross-platform, since version 6.0. The cross-platform version was originally called PowerShell Core, but the the word "Core" has been dropped since PowerShell 7.0+.
Get-ChildItem documentation: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem
A cleaner approach:
Get-ChildItem "<name_of_directory>" | where {$_.Attributes -match'Directory'}
I wonder if PowerShell 3.0 has a switch that only returns directories; it seems like a logical thing to add.
Use:
dir -r | where { $_ -is [System.IO.DirectoryInfo] }
From PowerShell v2 and newer (k represents the folder you are beginning your search at):
Get-ChildItem $Path -attributes D -Recurse
If you just want folder names only, and nothing else, use this:
Get-ChildItem $Path -Name -attributes D -Recurse
If you are looking for a specific folder, you could use the following. In this case, I am looking for a folder called myFolder:
Get-ChildItem $Path -attributes D -Recurse -include "myFolder"
Less text is required with this approach:
ls -r | ? {$_.mode -match "d"}
The accepted answer mentions
Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | Select-Object FullName
to get a "raw string".
But in fact objects of type Selected.System.IO.DirectoryInfo will be returned. For raw strings the following can be used:
Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | % { $_.FullName }
The difference matters if the value is concatenated to a string:
with Select-Object suprisingly foo\#{FullName=bar}
with the ForEach-operator the expected: foo\bar
Use:
dir -Directory -Recurse | Select FullName
This will give you an output of the root structure with the folder name for directories only.
You'll want to use Get-ChildItem to recursively get all folders and files first. And then pipe that output into a Where-Object clause which only take the files.
# one of several ways to identify a file is using GetType() which
# will return "FileInfo" or "DirectoryInfo"
$files = Get-ChildItem E:\ -Recurse | Where-Object {$_.GetType().Name -eq "FileInfo"} ;
foreach ($file in $files) {
echo $file.FullName ;
}
Use:
Get-ChildItem \\myserver\myshare\myshare\ -Directory | Select-Object -Property name | convertto-csv -NoTypeInformation | Out-File c:\temp\mydirectorylist.csv
Which does the following
Get a list of directories in the target location:
Get-ChildItem \\myserver\myshare\myshare\ -Directory
Extract only the name of the directories:
Select-Object -Property name
Convert the output to CSV format:
convertto-csv -NoTypeInformation
Save the result to a file:
Out-File c:\temp\mydirectorylist.csv
A bit more readable and simple approach could be achieved with the script below:
$Directory = "./"
Get-ChildItem $Directory -Recurse | % {
if ($_.Attributes -eq "Directory") {
Write-Host $_.FullName
}
}
Hope this helps!
My solution is based on the TechNet article Fun Things You Can Do With the Get-ChildItem Cmdlet.
Get-ChildItem C:\foo | Where-Object {$_.mode -match "d"}
I used it in my script, and it works well.
This question is well and truly answered but thought I'd add something extra as I've just been looking at this.
Get-ChildItem happens to produce two types of objects whereas most commands produce just one.
FileInfo and DirectoryInfo are returned.
You can see this by viewing the 'members' available to this command like so:
Get-ChildItem | Get-Member
TypeName: System.IO.DirectoryInfo
TypeName: System.IO.FileInfo
You'll see the various methods and properties available to each type. Note that there are differences. For example that the FileInfo object has a length property but the DirectoryInfo object doesn't.
Anyway, technically, we can return just the directories by isolating the DirectoryInfo object
Get-ChildItem | Where-Object {$_.GetType().Name -eq "DirectoryInfo"}
Obviously as the top answer states the most straightforward solution is to simply use Get-ChildItem -Directory but we now know how to work with multple object types in future :)
Use this one:
Get-ChildItem -Path \\server\share\folder\ -Recurse -Force | where {$_.Attributes -like '*Directory*'} | Export-Csv -Path C:\Temp\Export.csv -Encoding "Unicode" -Delimiter ";"
You can try the PsIsContainer Object
Get-ChildItem -path C:\mypath -Recurse | where {$_.PsIsContainer -eq $true}
To answer the original question specifically (using IO.FileAttributes):
Get-ChildItem c:\mypath -Recurse | Where-Object {$_.Attributes -band [IO.FileAttributes]::Directory}
I do prefer Marek's solution though:
Where-Object { $_ -is [System.IO.DirectoryInfo] }