.NET Framework или PowerShell не имеют явной поддержки для рекурсивных операций с файлами (включая загрузку). Вы должны реализовать рекурсию самостоятельно:
Трудная часть состоит в том, чтобы идентифицировать файлы из подкаталогов. Невозможно сделать это переносимым образом с платформой .NET (FtpWebRequest
или WebClient
). К сожалению, платформа .NET не поддерживает команду MLSD
, которая является единственным переносимым способом получения списка каталогов с атрибутами файлов в протоколе FTP. См. Также Проверка, является ли объект на FTP-сервере файлом или каталогом .
Ваши параметры:
LIST
command = ListDirectoryDetails
) и пытаетесь разобрать листинг на сервере. Многие FTP-серверы используют список * nix-style, в котором вы идентифицируете каталог по d
в самом начале записи. Но многие серверы используют другой формат. В следующем примере используется этот подход (при условии, что формат * nix) function DownloadFtpDirectory($url, $credentials, $localPath)
{
$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]
$localFilePath = Join-Path $localPath $name
$fileUrl = ($url + $name)
if ($permissions[0] -eq 'd')
{
if (!(Test-Path $localFilePath -PathType container))
{
Write-Host "Creating directory $localFilePath"
New-Item $localFilePath -Type directory | Out-Null
}
DownloadFtpDirectory ($fileUrl + "/") $credentials $localFilePath
}
else
{
Write-Host "Downloading $fileUrl to $localFilePath"
$downloadRequest = [Net.WebRequest]::Create($fileUrl)
$downloadRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$downloadRequest.Credentials = $credentials
$downloadResponse = $downloadRequest.GetResponse()
$sourceStream = $downloadResponse.GetResponseStream()
$targetStream = [System.IO.File]::Create($localFilePath)
$buffer = New-Object byte[] 10240
while (($read = $sourceStream.Read($buffer, 0, $buffer.Length)) -gt 0)
{
$targetStream.Write($buffer, 0, $read);
}
$targetStream.Dispose()
$sourceStream.Dispose()
$downloadResponse.Dispose()
}
}
}
Используйте следующую функцию:
$credentials = New-Object System.Net.NetworkCredential("user", "mypassword")
$url = "ftp://ftp.example.com/directory/to/download/"
DownloadFtpDirectory $url $credentials "C:\target\directory"
Код переводится с моего примера на C # в C # Загрузите все файлы и подкаталоги через FTP .
Если вы хотите избежать проблем с разбором форматов списков каталогов для конкретного сервера, используйте стороннюю библиотеку, которая поддерживает команда MLSD
и / или разбор различных форматов листинга LIST
; и рекурсивные загрузки.
Например, с помощью WinSCP .NET assembly вы можете загрузить целую директорию с помощью одного вызова Session.GetFiles
:
# 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
$session.GetFiles("/directory/to/download/*", "C:\target\directory\*").Check()
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
Внутри WinSCP использует команду MLSD
, если она поддерживается сервером.
Метод Session.GetFiles
по умолчанию рекурсивный.
(я автор WinSCP)
if ( cond) { yes } else { no }
является структурой управления. Он был разработан для создания программных вилок, а не для обработки последовательности. Я думаю, что многие люди пришли из SPSS или SAS, авторы которых выбрали «IF» для реализации условного присвоения в своих функциях DATA или TRANSFORM, и поэтому они ожидают, что R будет вести себя одинаково, тогда как R - из традиции программирования. R неявные for-loops встроены во множество векторизованных функций (включая ifelse
).
ifelse
принимает выражение, которое строит вектор логических значений в качестве первого аргумента. Второй и третий аргументы должны быть векторами равной длины, и либо первый из них, либо второй выбирается. Это похоже на команды SPSS / SAS IF
, которые имеют неявный режим работы по строке.
Из документации ifelse:
‘ifelse’ returns a value with the same shape as ‘test’ which is
filled with elements selected from either ‘yes’ or ‘no’ depending
on whether the element of ‘test’ is ‘TRUE’ or ‘FALSE’.
Таким образом, ваш вход имеет длину один, поэтому выход усечен до длины 1.
Вы также можете увидеть это проиллюстрировано более простым пример:
ifelse(TRUE, c(1, 3), 7)
# [1] 1
По какой-то причине это помечено как дубликат . Почему ifelse () возвращает однозначный вывод?
Итак, работа для этого вопроса:
a=3
yo <- ifelse(a==1, 1, list(c(1,2)))
yo[[1]]