PowerShell 能否使用 FTP 在一次传输中检索远程文件夹和子文件夹目录数据?
Can PowerShell use FTP to retrieve remote folder and subfolder directory data in a single transmission?
我有一个 PowerShell 脚本,它检查远程位置文件的现有时间戳数据,以便与本地文件进行比较。
使用此代码段检索本地信息:
$SrcEntries = Get-ChildItem $FolderLocation -Recurse
然后我可以使用以下代码段获取有关文件夹和文件的特定信息:
$Srcfolders = $SrcEntries | Where-Object{$_.PSIsContainer}
$SrcFiles = $SrcEntries | Where-Object{!$_.PSIsContainer}
远程信息当前使用此代码段检索,该代码段针对每个文件单独执行:
$Credentials = New-Object System.Net.NetworkCredential($FTPUser,$FTPPassword)
$FTPrequest = [System.Net.FtpWebRequest]::Create($RemoteFilePath)
$FTPrequest.Method = [System.Net.WebRequestMethods+FTP]::GetDateTimestamp
$FTPrequest.Credentials = $Credentials
$response = $FTPrequest.GetResponse().StatusDescription
然后解析 $response
以检索远程文件的时间戳。
以与检索本地数据相同的方式检索远程数据似乎更好,即在单个操作中使用上面的第一个代码片段,以避免多次往返远程服务器。
是否可以使用 PowerShell 在单个快速 FTP 操作中递归地获取文件夹和文件目录数据?我该怎么做?
如果没有任何外部库,实现起来并不容易。不幸的是,.NET Framework 和 PowerShell 都没有明确支持在 FTP 文件夹中递归列出文件。
您必须自己实施:
- 列出远程文件夹
- 迭代条目,递归到子文件夹中 - 再次列出它们,等等。
第一个棘手的部分是从子文件夹中识别文件。使用 .NET 框架 (FtpWebRequest
) 无法以可移植的方式做到这一点。不幸的是,.NET 框架不支持 MLSD
命令,这是在 FTP 协议中检索具有文件属性的目录列表的唯一可移植方式。另见 .
您的选择是:
- 对一个文件名进行操作,该操作肯定对文件失败而对目录成功(反之亦然)。 IE。你可以尝试下载“名字”。
- 你可能很幸运,在你的特定情况下,你可以通过文件名从目录中区分文件(即你的所有文件都有扩展名,而子目录没有)
- 您使用长目录列表(
LIST
命令= ListDirectoryDetails
方法)并尝试解析特定于服务器的列表。许多 FTP 服务器使用 *nix 样式列表,您可以在条目的最开头通过 d
识别目录。但是许多服务器使用不同的格式。下面的例子使用了这种方法(假设是 *nix 格式)
function ListFtpDirectory($url, $credentials)
{
$listRequest = [Net.WebRequest]::Create($url)
$listRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
$listRequest.Credentials = $credentials
$lines = New-Object System.Collections.ArrayList
$listResponse = $listRequest.GetResponse()
$listStream = $listResponse.GetResponseStream()
$listReader = New-Object System.IO.StreamReader($listStream)
while (!$listReader.EndOfStream)
{
$line = $listReader.ReadLine()
$lines.Add($line) | Out-Null
}
$listReader.Dispose()
$listStream.Dispose()
$listResponse.Dispose()
foreach ($line in $lines)
{
$tokens = $line.Split(" ", 9, [StringSplitOptions]::RemoveEmptyEntries)
$name = $tokens[8]
$permissions = $tokens[0]
if ($permissions[0] -eq 'd')
{
Write-Host "Directory $name"
$fileUrl = ($url + $name)
ListFtpDirectory ($fileUrl + "/") $credentials
}
else
{
Write-Host "File $name"
}
}
}
使用如下函数:
$credentials = New-Object System.Net.NetworkCredential("user", "mypassword")
$url = "ftp://ftp.example.com/directory/to/list/"
ListFtpDirectory $url $credentials
虽然上面的代码只会列出名字。由于您也需要时间戳,因此您甚至必须解析出这些时间戳。这与确定条目是文件夹还是文件一样复杂。
我没有相应的 PowerShell 代码,但请参阅我对其他类似问题的回答,其中显示了 C# 代码。转换为 PowerShell 应该很容易:
- Retrieving creation date of file (FTP)
- Parsing FtpWebRequest ListDirectoryDetails line
- C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response
如果您想避免解析特定于服务器的目录列表格式的麻烦,请使用支持 MLSD
命令 and/or 解析各种 LIST
列表格式的第 3 方库。
例如 WinSCP .NET assembly you can list whole directory recursively with a single call to Session.EnumerateRemoteFiles
:
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = [WinSCP.Protocol]::Ftp
HostName = "ftp.example.com"
UserName = "user"
Password = "mypassword"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$options =
[WinSCP.EnumerationOptions]::EnumerateDirectories -bor
[WinSCP.EnumerationOptions]::AllDirectories
$fileInfos = $session.EnumerateRemoteFiles(
"/directory/to/list", $Null, $options)
foreach ($fileInfo in $fileInfos)
{
Write-Host $fileInfo.FullName
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
不仅代码更简单、更健壮且与平台无关。它还可以通过 RemoteFileInfo
class.
轻松获得所有其他文件属性(大小、修改时间、权限、所有权)
如果服务器支持,WinSCP 在内部使用 MLSD
命令。如果没有,它使用 LIST
命令并支持数十种不同的列表格式。
虽然您的目的似乎是比较本地和远程时间戳来同步文件夹。
WinSCP .NET 程序集可以通过使用 Session.SynchronizeDirectories
.
的单个调用来做到这一点
您可能还会发现这些问题(由我回答)有用:
(我是WinSCP的作者)
我有一个 PowerShell 脚本,它检查远程位置文件的现有时间戳数据,以便与本地文件进行比较。
使用此代码段检索本地信息:
$SrcEntries = Get-ChildItem $FolderLocation -Recurse
然后我可以使用以下代码段获取有关文件夹和文件的特定信息:
$Srcfolders = $SrcEntries | Where-Object{$_.PSIsContainer}
$SrcFiles = $SrcEntries | Where-Object{!$_.PSIsContainer}
远程信息当前使用此代码段检索,该代码段针对每个文件单独执行:
$Credentials = New-Object System.Net.NetworkCredential($FTPUser,$FTPPassword)
$FTPrequest = [System.Net.FtpWebRequest]::Create($RemoteFilePath)
$FTPrequest.Method = [System.Net.WebRequestMethods+FTP]::GetDateTimestamp
$FTPrequest.Credentials = $Credentials
$response = $FTPrequest.GetResponse().StatusDescription
然后解析 $response
以检索远程文件的时间戳。
以与检索本地数据相同的方式检索远程数据似乎更好,即在单个操作中使用上面的第一个代码片段,以避免多次往返远程服务器。
是否可以使用 PowerShell 在单个快速 FTP 操作中递归地获取文件夹和文件目录数据?我该怎么做?
如果没有任何外部库,实现起来并不容易。不幸的是,.NET Framework 和 PowerShell 都没有明确支持在 FTP 文件夹中递归列出文件。
您必须自己实施:
- 列出远程文件夹
- 迭代条目,递归到子文件夹中 - 再次列出它们,等等。
第一个棘手的部分是从子文件夹中识别文件。使用 .NET 框架 (FtpWebRequest
) 无法以可移植的方式做到这一点。不幸的是,.NET 框架不支持 MLSD
命令,这是在 FTP 协议中检索具有文件属性的目录列表的唯一可移植方式。另见
您的选择是:
- 对一个文件名进行操作,该操作肯定对文件失败而对目录成功(反之亦然)。 IE。你可以尝试下载“名字”。
- 你可能很幸运,在你的特定情况下,你可以通过文件名从目录中区分文件(即你的所有文件都有扩展名,而子目录没有)
- 您使用长目录列表(
LIST
命令=ListDirectoryDetails
方法)并尝试解析特定于服务器的列表。许多 FTP 服务器使用 *nix 样式列表,您可以在条目的最开头通过d
识别目录。但是许多服务器使用不同的格式。下面的例子使用了这种方法(假设是 *nix 格式)
function ListFtpDirectory($url, $credentials)
{
$listRequest = [Net.WebRequest]::Create($url)
$listRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
$listRequest.Credentials = $credentials
$lines = New-Object System.Collections.ArrayList
$listResponse = $listRequest.GetResponse()
$listStream = $listResponse.GetResponseStream()
$listReader = New-Object System.IO.StreamReader($listStream)
while (!$listReader.EndOfStream)
{
$line = $listReader.ReadLine()
$lines.Add($line) | Out-Null
}
$listReader.Dispose()
$listStream.Dispose()
$listResponse.Dispose()
foreach ($line in $lines)
{
$tokens = $line.Split(" ", 9, [StringSplitOptions]::RemoveEmptyEntries)
$name = $tokens[8]
$permissions = $tokens[0]
if ($permissions[0] -eq 'd')
{
Write-Host "Directory $name"
$fileUrl = ($url + $name)
ListFtpDirectory ($fileUrl + "/") $credentials
}
else
{
Write-Host "File $name"
}
}
}
使用如下函数:
$credentials = New-Object System.Net.NetworkCredential("user", "mypassword")
$url = "ftp://ftp.example.com/directory/to/list/"
ListFtpDirectory $url $credentials
虽然上面的代码只会列出名字。由于您也需要时间戳,因此您甚至必须解析出这些时间戳。这与确定条目是文件夹还是文件一样复杂。
我没有相应的 PowerShell 代码,但请参阅我对其他类似问题的回答,其中显示了 C# 代码。转换为 PowerShell 应该很容易:
- Retrieving creation date of file (FTP)
- Parsing FtpWebRequest ListDirectoryDetails line
- C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response
如果您想避免解析特定于服务器的目录列表格式的麻烦,请使用支持 MLSD
命令 and/or 解析各种 LIST
列表格式的第 3 方库。
例如 WinSCP .NET assembly you can list whole directory recursively with a single call to Session.EnumerateRemoteFiles
:
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = [WinSCP.Protocol]::Ftp
HostName = "ftp.example.com"
UserName = "user"
Password = "mypassword"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$options =
[WinSCP.EnumerationOptions]::EnumerateDirectories -bor
[WinSCP.EnumerationOptions]::AllDirectories
$fileInfos = $session.EnumerateRemoteFiles(
"/directory/to/list", $Null, $options)
foreach ($fileInfo in $fileInfos)
{
Write-Host $fileInfo.FullName
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
不仅代码更简单、更健壮且与平台无关。它还可以通过 RemoteFileInfo
class.
如果服务器支持,WinSCP 在内部使用 MLSD
命令。如果没有,它使用 LIST
命令并支持数十种不同的列表格式。
虽然您的目的似乎是比较本地和远程时间戳来同步文件夹。
WinSCP .NET 程序集可以通过使用 Session.SynchronizeDirectories
.
您可能还会发现这些问题(由我回答)有用:
(我是WinSCP的作者)