FtpHandling/Get-FTPChildItem.ps1
<#
.SYNOPSIS Gets the item and child items from ftp location. .DESCRIPTION The Get-FTPChildItem cmdlet gets the items from ftp locations. If the item is a container, it gets the items inside the container, known as child items. .PARAMETER Path Specifies a path to ftp location or file. .PARAMETER Session Specifies a friendly name for the ftp session. Default session name is 'DefaultFTPSession'. .PARAMETER Recurse Get recurse child items. .PARAMETER Depth Define depth of folder in recurse mode. Autoenable recurse mode. .PARAMETER Filter Specifies a filter parameter to return only this objects that have proper name. This parameter allow to use of wildcards. Default value is *. .EXAMPLE PS P:\> Get-FTPChildItem -path ftp://ftp.contoso.com/folder Parent: ftp://ftp.contoso.com/folder Dir Right Ln User Group Size ModifiedDate Name --- ----- -- ---- ----- ---- ------------ ---- d rwxr-xr-x 3 ftp ftp 2012-06-19 12:58:00 subfolder1 d rwxr-xr-x 2 ftp ftp 2012-06-19 12:58:00 subfolder2 - rw-r--r-- 1 ftp ftp 1KB 2012-06-15 12:49:00 textitem.txt .EXAMPLE PS P:\> Get-FTPChildItem -path ftp://ftp.contoso.com/folder -Filter "subfolder*" Parent: ftp://ftp.contoso.com/folder Dir Right Ln User Group Size ModifiedDate Name --- ----- -- ---- ----- ---- ------------ ---- d rwxr-xr-x 3 ftp ftp 2012-06-19 12:58:00 subfolder1 d rwxr-xr-x 2 ftp ftp 2012-06-19 12:58:00 subfolder2 .EXAMPLE PS P:\> Get-FTPChildItem -path folder -Recurse Parent: ftp://ftp.contoso.com/folder Dir Right Ln User Group Size ModifiedDate Name --- ----- -- ---- ----- ---- ------------ ---- d rwxr-xr-x 3 ftp ftp 2012-06-19 12:58:00 subfolder1 d rwxr-xr-x 2 ftp ftp 2012-06-19 12:58:00 subfolder2 - rw-r--r-- 1 ftp ftp 1KB 2012-06-15 12:49:00 textitem.txt Parent: ftp://ftp.contoso.com/folder/subfolder1 Dir Right Ln User Group Size ModifiedDate Name --- ----- -- ---- ----- ---- ------------ ---- d rwxr-xr-x 2 ftp ftp 2012-06-19 12:58:00 subfolder11 - rw-r--r-- 1 ftp ftp 21KB 2012-06-19 09:20:00 test.xlsx - rw-r--r-- 1 ftp ftp 14KB 2012-06-19 11:27:00 ziped.zip Parent: ftp://ftp.contoso.com/folder/subfolder1/subfolder11 Dir Right Ln User Group Size ModifiedDate Name --- ----- -- ---- ----- ---- ------------ ---- - rw-r--r-- 1 ftp ftp 14KB 2012-06-19 11:27:00 ziped.zip Parent: ftp://ftp.contoso.com/folder/subfolder2 Dir Right Ln User Group Size ModifiedDate Name --- ----- -- ---- ----- ---- ------------ ---- - rw-r--r-- 1 ftp ftp 1KB 2012-06-15 12:49:00 textitem.txt - rw-r--r-- 1 ftp ftp 14KB 2012-06-19 11:27:00 ziped.zip .EXAMPLE PS P:\> $ftpFile = Get-FTPChildItem -path /folder/subfolder1/test.xlsx PS P:\> $ftpFile | Select-Object Parent, Name, ModifiedDate Parent Name ModifiedDate ------ ---- ------------ ftp://ftp.contoso.com/folder/subfolder1 test.xlsx 2012-06-19 09:20:00 .NOTES Author: Michal Gajda Blog : http://commandlinegeeks.com/ .LINK Set-FTPConnection #> Function Get-FTPChildItem { [OutputType('PSFTP.Item')] [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="Low" )] Param( [parameter(ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true)] [String]$Path = "", $Session = "DefaultFTPSession", [parameter(ValueFromPipelineByPropertyName=$true)] [Switch]$Recurse, [Int]$Depth = 0, [String]$Filter = "*" ) Begin { if($Session -isnot [String]) { $CurrentSession = $Session } else { $CurrentSession = Get-Variable -Scope Global -Name $Session -ErrorAction SilentlyContinue -ValueOnly } if($null -eq $CurrentSession) { Write-Warning "Add-FTPItem: Cannot find session $Session. First use Set-FTPConnection to config FTP connection." Break Return } } Process { Write-Debug "Native path: $Path" if($Path -match "ftp://") { $RequestUri = $Path Write-Verbose "Use original path: $RequestUri" } else { $RequestUri = $CurrentSession.RequestUri.OriginalString+"/"+$Path Write-Verbose "Add ftp:// at start: $RequestUri" } $RequestUri = [regex]::Replace($RequestUri, '/$', '') $RequestUri = [regex]::Replace($RequestUri, '/+', '/') $RequestUri = [regex]::Replace($RequestUri, '^ftp:/', 'ftp://') Write-Verbose "Remove additonal slash: $RequestUri" if($Depth -gt 0) { $CurrentDepth = [regex]::matches($RequestUri,"/").count if($null -eq (Get-Variable -Scope Script -Name MaxDepth -ErrorAction SilentlyContinue)) { New-Variable -Scope Script -Name MaxDepth -Value ([Int]$CurrentDepth +$Depth) } Write-Verbose "Auto enable recurse mode. Current depth / Max Depth: $CurrentDepth / $($Script:MaxDepth)" $Recurse = $true } if ($pscmdlet.ShouldProcess($RequestUri,"Get child items from ftp location")) { if((Get-FTPItemSize $RequestUri -Session $Session -Silent) -eq -1) { Write-Verbose "Path is directory" $ParentPath = $RequestUri } else { Write-Verbose "Path is file. Delete last file name to get parent path." $LastIndex = $RequestUri.LastIndexOf("/") $ParentPath = $RequestUri.SubString(0,$LastIndex) } [System.Net.FtpWebRequest]$Request = [System.Net.WebRequest]::Create($RequestUri) $Request.Credentials = $CurrentSession.Credentials $Request.EnableSsl = $CurrentSession.EnableSsl $Request.KeepAlive = $CurrentSession.KeepAlive $Request.UseBinary = $CurrentSession.UseBinary $Request.UsePassive = $CurrentSession.UsePassive $Request.Method = [System.Net.WebRequestMethods+FTP]::ListDirectoryDetails Write-Verbose "Use WebRequestMethods: $($Request.Method)" Try { $mode = "Unknown" [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$CurrentSession.ignoreCert} $Response = $Request.GetResponse() [System.IO.StreamReader]$Stream = New-Object System.IO.StreamReader($Response.GetResponseStream(),[System.Text.Encoding]::Default) $DirList = @() $ItemsCollection = @() Try { [string]$Line = $Stream.ReadLine() Write-Debug "Read Line: $Line" } Catch { $Line = $null Write-Debug "Line is null" } While ($Line) { if($mode -eq "Compatible" -or $mode -eq "Unknown") { $null, [string]$IsDirectory, [string]$Flag, [string]$Link, [string]$UserName, [string]$GroupName, [string]$Size, [string]$Date, [string]$Name = ` [regex]::split($Line,'^([d-])([rwxt-]{9})\s+(\d{1,})\s+([.@A-Za-z0-9-]+)\s+([A-Za-z0-9-]+)\s+(\d{1,})\s+(\w+\s+\d{1,2}\s+\d{1,2}:?\d{2})\s+(.+?)\s?$',"SingleLine,IgnoreCase,IgnorePatternWhitespace") if($IsDirectory -eq "" -and $mode -eq "Unknown") { $mode = "IIS6" } elseif($mode -ne "Compatible") { $mode = "Compatible" #IIS7/Linux } if($mode -eq "Compatible") { $DatePart = $Date -split "\s+" $NewDateString = "$($DatePart[0]) $('{0:D2}' -f [int]$DatePart[1]) $($DatePart[2])" Try { if($DatePart[2] -match ":") { $Month = ([DateTime]::ParseExact($DatePart[0],"MMM",[System.Globalization.CultureInfo]::InvariantCulture)).Month if((Get-Date).Month -ge $Month) { $NewDate = [DateTime]::ParseExact($NewDateString,"MMM dd HH:mm",[System.Globalization.CultureInfo]::InvariantCulture) } else { $NewDate = ([DateTime]::ParseExact($NewDateString,"MMM dd HH:mm",[System.Globalization.CultureInfo]::InvariantCulture)).AddYears(-1) } } else { $NewDate = [DateTime]::ParseExact($NewDateString,"MMM dd yyyy",[System.Globalization.CultureInfo]::InvariantCulture) } } Catch { Write-Verbose "Can't parse date: $Date" } } } if($mode -eq "IIS6") { $null, [string]$NewDate, [string]$IsDirectory, [string]$Size, [string]$Name = ` [regex]::split($Line,'^(\d{2}-\d{2}-\d{2}\s+\d{2}:\d{2}[AP]M)\s+<*([DIR]*)>*\s+(\d*)\s+(.+).*$',"SingleLine,IgnoreCase") if($IsDirectory -eq "") { $IsDirectory = "-" } } Switch($Size) { {[int64]$_ -lt 1024} { $HFSize = $_+"B"; break } {[System.Math]::Round([int64]$_/1KB,0) -lt 1024} { $HFSize = [String]([System.Math]::Round($_/1KB,0))+"KB"; break } {[System.Math]::Round([int64]$_/1MB,0) -lt 1024} { $HFSize = [String]([System.Math]::Round($_/1MB,0))+"MB"; break } {[System.Math]::Round([int64]$_/1GB,0) -lt 1024} { $HFSize = [String]([System.Math]::Round($_/1GB,0))+"GB"; break } {[System.Math]::Round([int64]$_/1TB,0) -lt 1024} { $HFSize = [String]([System.Math]::Round($_/1TB,0))+"TB"; break } {[System.Math]::Round([int64]$_/1PB,0) -lt 1024} { $HFSize = [String]([System.Math]::Round($_/1PB,0))+"PB"; break } } if($IsDirectory -eq "d" -or $IsDirectory -eq "DIR") { $HFSize = "" } if($ParentPath -match "\*|\?") { $LastIndex = $ParentPath.LastIndexOf("/") $ParentPath = $ParentPath.SubString(0,$LastIndex) $ParentPath.Trim() + "/" + $Name.Trim() } $LineObj = New-Object PSObject -Property @{ Dir = $IsDirectory Right = $Flag Ln = $Link User = $UserName Group = $GroupName Size = $HFSize SizeInByte = $Size OrgModifiedDate = $Date ModifiedDate = $NewDate Name = $Name.Trim() FullName = $ParentPath.Trim() + "/" + $Name.Trim() Parent = $ParentPath.Trim() } $LineObj.PSTypeNames.Clear() $LineObj.PSTypeNames.Add('PSFTP.Item') if($Recurse -and ($LineObj.Dir -eq "d" -or $LineObj.Dir -eq "DIR")) { $DirList += $LineObj } if($LineObj.Dir) { if($LineObj.Name -like $Filter) { Write-Debug "Filter accepted: $Filter" $ItemsCollection += $LineObj } } $Line = $Stream.ReadLine() Write-Debug "Read Line: $Line" } $Response.Close() if($Recurse -and ($CurrentDepth -lt $Script:MaxDepth -or $Depth -eq 0)) { $RecurseResult = @() $DirList | ForEach-Object { Write-Debug "Recurse is active and go to: $($_.FullName)" $RecurseResult += Get-FTPChildItem -Path ($_.FullName) -Session $Session -Recurse -Filter $Filter -Depth $Depth } $ItemsCollection += $RecurseResult } if($ItemsCollection.count -eq 0) { Return } else { Return $ItemsCollection | Sort-Object -Property @{Expression="Parent";Descending=$false}, @{Expression="Dir";Descending=$true}, @{Expression="Name";Descending=$false} } } Catch { Write-Error $_.Exception.Message -ErrorAction Stop } } if($CurrentDepth -ge $Script:MaxDepth) { Remove-Variable -Scope Script -Name CurrentDepth } } End{} } Export-ModuleMember Get-FTPChildItem |