1

我想避免移动当前由另一个进程打开的文件。move-item PowerShell 命令有什么方法可以移动,或者更糟糕的是复制当前打开的文件?

我们目前的情况是,我们有两个进程需要将数据文件从进程 A 的输出文件夹传输到进程 B 的输入文件夹。这个想法是进程 A 写入一个文件,然后 PowerShell 脚本将文件移动到进程 B 读取的文件夹中。

我们有时会遇到一个问题,即同一个文件被传输了两次,而且这两次都不是部分文件。

以下代码在整点后的 00、10、20、30、40、50 分钟执行。Samba服务器上的进程 B 在05、15、25、35、45、55分钟后运行,一旦进程 B 处理完文件,就会将文件移出 PowerShell 脚本放入的文件夹。一次最多只能移动十几个 1 KB 的文件。

进程 A 不受我们控制,可以随时将文件写入该位置。似乎在 PowerShell 脚本移动文件之前,进程 A 创建文件时发生了一些竞争情况,脚本复制文件,然后在 10 分钟后脚本再次运行时移动它。

使用下面的代码,如果在日志文件中为同一个文件记录了两个条目,并且在“已移动文件”中记录了两个条目,那么进程 A 唯一可能会创建两次文件吗?

$source = "C:\folder\*.txt"
$target_dir = "\\samba-server\share\"
$bad_dir = "C:\folder\bad_files\"
$log = "C:\SystemFiles\Logs\transfer.log"

$files = Get-ChildItem $source
foreach ($file in $files){

    if ($file.name -eq $null) {
        # Nothing to do, Added this in since for some reason it executes the conditions below
    }
    elseif (test-path ($target_dir + $file.name)) {

        # If there is a duplicate file, write to the log file, then copy it to the bad dir with
        # the datetime stamp in front of the file name

        $log_string = ((Get-Date -format G) + ",Duplicate File," + "'" + $file.name + "', " + $file.LastWriteTime)
        write-output ($log_string) >> $log
        $new_file = ($bad_dir + (get-date -format yyyy.MM.dd.HHmmss) + "_" + $file.name)
        move-item $file.fullname $new_file
    }
    else {
        # The file doesnt exist on the remote source, so we are good to move it.

        move-item $file.fullname $target_dir
        if ($?) { # If the last command completed successfully
            $log_string = ((Get-Date -format G) + ",Moved File," + "'" + $file.name + "', " + $file.LastWriteTime)
        } else {
            $log_string = ((Get-Date -format G) + ",Failed to Move File," + "'" + $file.name + "', " + $file.LastWriteTime)
        }
        write-output ($log_string) >> $log
    }
}
4

1 回答 1

3

这是经典的生产者-消费者问题,这是一个很好的研究课题。

您可能会尝试的一些解决方案是检查文件的上次写入时间。如果它在过去足够好,它可以毫无问题地移动。另一个将尝试以独占访问权限打开文件。如果失败,则该文件仍被生产者进程使用。否则,关闭文件并移动它。

有些例子是这样的,

# List files that were modified at least five minutes ago
gci | ? { $_.lastwritetime -le (get-date).addminutes(-5) }

# Try to open a file with exclusive mode
try {

    $f1 = [IO.File]::Open("c:\temp\foo.txt", [IO.Filemode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::None)
    # If that didn't fail, close and move the file to new location
    $f1.Close()
    $f1.Dispose()
    Move-Item "c:\temp\foo.txt" $newLocation

} catch [System.IO.IOException]{
    "File is already open" # Catch the file is locked exception, try again later
}
于 2013-07-02T05:05:06.263 回答