Windows 上的命令行从 FTP 站点获取最新文件

Command line on Windows to get latest files from FTP site

我需要 Windows 上命令行的批处理脚本 运行,它会从 FTP 站点下载最新文件(有时最新文件可能不止一个文件)。

我使用了 WinSCP.com 中的以下命令,但我只得到一个最新文件。可以给我一些指导吗?

get -latest *.txt

Wxample:使用上面的命令我只得到文件名 XYZ.txt 文件,我希望有一个脚本能够同时获得 ABC.txtZZZ.txt.

filename        last written date
------------    ----------------------
ABC.txt         2016-01-01
ZZZ.txt         2016-01-01
A123.txt        2015-12-10

没有基于日期下载多个文件的内置命令,我们必须手动完成。

标准 ls 输出是人类可读的,但不能按日期排序:

D---------   0                           0 Aug 21 15:32:37 2016 Default

我们需要使用 /xmllog= 参数的 xml 日志:

<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="........." name="USER@localhost" start="2016-08-23T15:41:10.708Z">
  <ls>
    <destination value="/temp" />
    <files>
      <file>
        <filename value=".." />
        <type value="D" />
        <modification value="1899-12-30T02:00:00.000Z" />
        <permissions value="---------" />
      </file>

很好,每个文件都有一个机器可读的日期、名称、类型(D 代表目录,我们将跳过它们)。


批处理文件。
(注释分隔符也是代码的一部分)

@echo off

: Setup

setlocal enableDelayedExpansion
set "WinSCP=C:\somepath\WinSCP.com"
set "ftp=ftp://USER:PASSWORD@ftp.server.com"
set "remoteFolder=/someFolderNoTrailingSlash"

: List all ftp files

"%WinSCP%" ^
    /command "open %ftp%" "ls ""%remoteFolder%""" "exit" ^
    /xmllog="%temp%\ftplog.xml" >nul
(
    set isDirectory=
    for /f "tokens=1,3 delims=<= " %%a in ('type "%temp%\ftplog.xml"') do (
        if "%%a"=="filename"     (set "line=%%~b" & set isDirectory=)
        if "%%a"=="type" if "%%~b"=="D" (set isDirectory=yes)
        if "%%a"=="modification" if not defined isDirectory (
            set "line=%%~b*!line!"
            echo !line!
        )
    )
) > "%temp%\ftplist.txt"

: Sort by date and make a WinSCP script to get the latest files with same date

set lastDate=
(
    echo open %ftp%
    for /f "delims=* tokens=1,2" %%a in ('
        sort /reverse "%temp%\ftplist.txt"
    ') do (
        for /f "delims=T" %%c in ("%%a") do set thisDate=%%c
        if defined lastDate (
            if not !thisDate!==!lastDate! goto download
        ) else (
            set lastDate=!thisDate!
        )
        echo get -preservetime "%remoteFolder%/%%b"
    )
) > "%temp%\ftpscript.txt"
if not defined lastDate goto cleanup

: Download and cleanup temporary files

:download
"%WinSCP%" /script="%temp%\ftpscript.txt" /command "exit"

:cleanup
del "%temp%\ftplog.xml" "%temp%\ftplist.txt" "%temp%\ftpscript.txt"

pause

虽然这可以通过普通的 WinSCP 脚本和批处理文件来实现,因为 shows, the code would be much more readable in a more advanced language like PowerShell with a use of the WinSCP .NET assembly

下面的代码部分基于示例 Downloading the most recent file

param (
    $session = "ftp://USER:PASSWORD@ftp.server.com",
    $remotePath = "/remote/path",
    $localPath = "C:\local\path"
)

try
{
    Add-Type -Path "WinSCPnet.dll"

    # Setup session options
    $sessionOptions = New-Object WinSCP.SessionOptions
    $sessionOptions.ParseUrl($session)

    try
    {
        Write-Host "Connecting..."
        $session = New-Object WinSCP.Session

        $session.Open($sessionOptions)

        $files = $session.ListDirectory($remotePath).Files
 
        $latest_file =
            $files |
            Where-Object { -Not $_.IsDirectory } |
            Sort-Object LastWriteTime -Descending |
            Select-Object -First 1

        Write-Host (
            "Latest file is $latest_file created on $($latest_file.LastWriteTime)")
        
        $day = Get-Date -date $latest_file.LastWriteTime -hour 0 -minute 0 -second 0

        $latest_files =
            $files |
            Where-Object { -Not $_.IsDirectory } |
            Where-Object { $_.LastWriteTime -ge $day }

        Write-Host "Downloading $($latest_files.Count) files created after $day"

        foreach ($file in $latest_files)
        {
            $path = $file.FullName
            Write-Host $path
            $session.GetFiles(
                [WinSCP.RemotePath]::EscapeFileMask($path),
                (Join-Path $localPath $file.Name)).Check()
        }

        Write-Host "Done"
    }
    finally
    {
        # Disconnect, clean up
        $session.Dispose()
    }

    exit 0
}
catch [Exception]
{
    Write-Host $_.Exception.Message
    exit 1
}