How to use Test-Path including a regex - powershell

I would like to check if the file Test.txt exists in a specific directory (folder name with 16-digits).
I tried for example following command:
Test-Path "C:\Users\<USERNAME>\Desktop\Test\([0-9]{16})\Test.txt"
Following command cannot be used in my situation.
Test-Path "C:\Users\<USERNAME>\Desktop\Test\*\Test.txt"
Thank you!

This would be one way of doing that:
# This is for PowerShell version 3.0 and up. If you are using an older version, use
# Get-ChildItem -Path "C:\Users\<USERNAME>\Desktop\Test" | Where-Object { $_.PSIsContainer -and $_.Name -match '\d{16}' }
Get-ChildItem -Path "C:\Users\<USERNAME>\Desktop\Test" -Directory | Where-Object { $_.Name -match '\d{16}' } |
ForEach-Object {
$fileName = Join-Path -Path $_.FullName -ChildPath 'Test.txt'
if (Test-Path -Path $fileName -PathType Leaf) {
Write-Host "$fileName exists"
}
else {
Write-Host "$fileName does not exist"
}
}

Test-Path will return $True/$False, to achieve something similar you can do:
((Get-ChildItem "C:\Users\<USERNAME>\Desktop\Test\*\Test.txt" |
where FullName -match "\\Test\\\d{16}\\Test.txt$").Count -gt 0)
But that will not reveal which folders matched.

Related

Powershell - Select-String Advice needed

My goal is to create a PS script that does the following:
a) scans a source directory and produces a list of files that have a matching pattern that I pass in found inside the files
b) take the list of those files and Copy-Item to move and then archive it.
I have this process working for when I "-Filter" on a filename, but can't seem to get my script to work when using "Select-String -pattern". When it gets to the "$FileNames = #($Files | %{$_.Path.Substring($Source.Length)})" part of the code, it says file does not exist as its passing in the #{Path} code?
View of error
If ({$PatternIdentifier -ne "" -and $FileIdentifier -eq "" -and $FileExtension -ne ""})
{
$Files = get-childitem $Source -Filter $FileExtension | Select-String -pattern $PatternIdentifier -SimpleMatch |Select Path
}
$FileNames = #($Files | %{$_.Path.Substring($Source.Length)})
if($Files.Count -ne 0)
{
if ((test-path $ArchiveDestination) -eq 0)
{
New-Item -ItemType Directory -Force -Path $ArchiveDestination
}
foreach ($File in $Files)
{
Copy-Item $File -Destination $DestinationFolder
Copy-Item $File -Destination $ArchiveDestination
$count++
}
if($error.length -lt 0)
{
Write-Host ("Copied {0} files!!" -f $count)
$answer = $FilesTotalCount -$count
}
}
Get the value from the property:
|Select -expand path

Powershell Unzip One Specific Folder in a File with Dynamic Name

I found a piece of code here that almost does what I need, which is extract just one folder from an archived file.
The only issue I have is that the archive name changes month on month, therefore I wanted to use a wildcard. Once a wildcard is specified (* in $zipfile), the script does not work for me.
I would be grateful for any suggestions.
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null
$zipfile = 'C:\ALL\Debtor*.zip'
$folder = 'tmp\st\sd'
$dst = 'C:\ALL\ZipOutput'
[IO.Compression.ZipFile]::OpenRead($zipfile).Entries | ? {
$_.FullName -like "$($folder -replace '\\','/')/*.*"
} | % {
$file = Join-Path $dst $_.FullName
$parent = Split-Path -Parent $file
if (-not (Test-Path -LiteralPath $parent)) {
New-Item -Path $parent -Type Directory | Out-Null
}
[IO.Compression.ZipFileExtensions]::ExtractToFile($_, $file, $true)
Try this out for size. Just use Get-ChildItem to locate the zip file in your ALL directory.
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null
$zipfile = Get-ChildItem -Path C:\ALL\ -Filter *.zip | Where-Object {$_.Name -like "Debtor*"} | Select-Object -ExpandProperty FullName
$folder = 'tmp\st\sd\'
$dst = 'C:\ALL\ZipOutput'
[IO.Compression.ZipFile]::OpenRead($zipfile).Entries | Where-Object {
$_.FullName -like "$($folder -replace '\\','/')/*.*"
} | ForEach-Object {
$file = Join-Path $dst $_.FullName
$parent = Split-Path -Parent $file
if(-not (Test-Path -LiteralPath $parent)) {
New-Item -Path $parent -Type Directory | Out-Null
}
[IO.Compression.ZipFileExtensions]::ExtractToFile($_, $file, $true)
}
I am also assuming that the archive name changes but there are not multiple archives with that name. If there are you will need to wrap everything in a Foreach($zip in $zipfile){ ... }

Post-order Traverse in PowerShell recurse

Get-ChildItem -Recurse in powershell currently traverse a directory in level order fashion. Is there any way to traverse a directory in post-order way in Powershell?
I am trying to delete files which are older than certain times. and after deleting files, if subfolder is empty, delete that folder too. Right now am doing this.
$path = 'D:\Files'
Get-ChildItem -Path $path -Recurse | Where-Object {
(($_.LastWriteTime -lt (Get-Date).AddDays(-30)) -and ($_ -is [system.io.fileinfo]) )
} | Remove-Item
Get-ChildItem -Path $path -Recurse | Where-Object {
($_ -is [System.IO.DirectoryInfo]) -and $_.CreationTime -lt (Get-Date).AddDays(-30) -and ((Get-ChildItem $_.FullName).Count -eq 0)
} | Remove-Item -Force
But I want to do it in a single command. Not as two different command.
You could reverse the order of the items returned by Get-ChildItem with [Array]::Reverse
Full script:
$items = Get-ChildItem 'D:\Files' -Recurse
[Array]::Reverse($items)
$date = (Get-Date).AddDays(-30)
foreach ($item in $items) {
if ($item.PSIsContainer) {
if ($item.CreationTime -lt $date -and (Get-ChildItem $item.FullName).Count -eq 0) {
Remove-Item $item.FullName
}
}
elseif ($item.LastWriteTime -lt $date) {
Remove-Item $item.FullName
}
}
I couldn't get the post order working properly with GCI, someone claimed it should, but it wasn't traversing depth first. Below is a naive implementation of the classic post order algorithm using the push directory and pop directory commands. Put your "actions" where Write-Host is.
function PostOrder($d){
pushd $d
$folders = Get-ChildItem .\ -Directory -Force
foreach ($folder in $folders){
PostOrder($folder)
}
popd
Write-Host $d.FullName
}
PostOrder("C:\myFolder")

find all specific folders and zip them

I want to find all folders with name 'test' and to zip them into single folder with different names, of course.
I managed to do some code:
$RootFolder = "E:\"
$var = Get-ChildItem -Path $RootFolder -Recurse |
where {$_.PSIsContainer -and $_.Name -match 'test'}
#this is assembly for zip functionality
Add-Type -Assembly "System.IO.Compression.Filesystem"
foreach ($dir in $var) {
$destination = "E:\zip\test" + $dir.Name + ".zip"
if (Test-Path $destination) {Remove-Item $destination}
[IO.Compression.Zipfile]::CreateFromDirectory($dir.PSPath, $destination)
}
It gives me an error:
Exception calling "CreateFromDirectory" with "2" argument(s): "The given path's format is not supported."
I want to know, what is the right way to pass path of my $dir.
The PSPath property returned from Get-ChildItem is prefixed with the PSProvider. The CreateFromDirectory() method takes two strings; the first is sourceDirectoryName for which you could use Fullname from your object.
$RootFolder = "E:\"
$Directories = Get-ChildItem -Path $RootFolder -Recurse | Where-Object {
$_.PSIsContainer -And
$_.BaseName -Match 'test'
}
Add-Type -AssemblyName "System.IO.Compression.FileSystem"
foreach ($Directory in $Directories) {
$Destination = "E:\zip\test$($Directory.name).zip"
If (Test-path $Destination) {
Remove-Item $Destination
}
[IO.Compression.ZipFile]::CreateFromDirectory($Directory.Fullname, $Destination)
}
If you're using v5, I would suggest using the Commandlet
If you don't want to use the commandlet you can use this:
$FullName = "Path\FileName"
$Name = CompressedFileName
$ZipFile = "Path\ZipFileName"
$Zip = [System.IO.Compression.ZipFile]::Open($ZipFile,'Update')
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($Zip,$FullName,$Name,"optimal")
$Zip.Dispose()
If you have a folder structure like this:
- Folder1
-- Test
- Folder2
-- Test
- Folder3
-- Test
You can do this:
gci -Directory -Recurse -Filter 'test*' | % {
Compress-Archive "$($_.FullName)\**" "$($_.FullName -replace '\\|:', '.' ).zip"
}
And you will get:
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder1.Test.zip
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder2.Test.zip
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder3.Test.zip
Or if you want to preserve the directory structure inside your zips:
gci -Directory -Recurse -Filter 'test*' | % {
Compress-Archive $_.FullName "$($_.FullName -replace '\\|:', '.' ).zip"
}

Trying to move a folder(s) if they exist

I'm not getting the if/else state to work. I'm trying to move a folder or folders that starts with numbers 0-9. If the folders are there the folders will be moved. But im trying to make an else statement if they dont exists. Here is my script:
$Source = "\\Server\share"
$Destination = "\\Server\Archive"
$Dir = Get-ChildItem $Source | Where-Object {$_.Name -match "\d"}
if (!(Test-Path $Source | Where-Object {$_.Name -match "\d"}))
{
Move-Item $Dir -Destination $Destination
}
else
{
Write-Host "No folder to backup"
}
Anyone have any good suggestion how I should solve this or use a different method?
Going out on a limb my guess would be that you want to check if $Source contains one or more subfolder(s) whose name starts with a digit, and move those that exist to $Destination. If that assumption is correct I'd suggest to do the following:
$Source = '\\Server\share'
$Destination = '\\Server\Archive'
$Dir = Get-ChildItem $Source -Directory | Where-Object {$_.Name -match '^\d'}
if ($Dir) {
$Dir | Move-Item -Destination $Destination
} else {
Write-Host 'No folder to backup'
}