Powershell Copy-Item为同一路径提供2个不同的结果

时间:2018-06-05 15:18:36

标签: powershell powershell-v5.0

问题

我正在使用PowerShell编写部署脚本。使用Copy-Item命令在运行之间不会提供相同的结果,即使使用相同的数据也是如此。 (它似乎不是幂等的)

请参阅下面的结果,了解我的意思。我意识到我可以在复制“新”版本之前删除C:\products中的文件,但我更好奇这是否是Powershell中的预期行为。 (而且我假设它是)

我宁愿学习使用Copy-Item的'正确'方法,而不是在我缺乏知识的情况下进行黑客攻击。

我尝试使用target\*路径代替target,但是我将lib文件夹分解为showcase的根目录,我甚至没想到-Recurse标志。

C:\products
  - showcase
    - showcase.jar
    - logback.jar
    - foo.jar
    - junit.jar
    - etc, etc

我也对文档here(特别是示例7,10和11)感到困惑,因为7使用-Recurse标志而11则不使用C:\products - backup 标志。同时示例10说:

  

如果脚本文件夹包含子文件夹中的文件,那么这些子文件夹将被复制,文件树完好无损。

但是从不指定递归标记。

结果

部署前

C:\products
  - showcase
    - showcase.jar
    - lib
      - <dependencies for showcase.jar>
  - backup
    <empty>
# Expected, as there wasn't a prior deployment yet.

初始部署

C:\products
  - showcase
    - showcase.jar
    - lib
      - <dependencies for showcase.jar>
    - target
      - showcase.jar
      - lib
        - <dependencies for inner showcase.jar>
  - backup
    - showcase
      - showcase.jar
      - lib
        - <dependencies for "old" showcase.jar>
  # Expected, the old version was backed up

第二次部署

./deploy.ps1 -hostname foobar (it's a remote server)

部署脚本及其调用方式

param( [String]$hostname, [switch]$debug )

$folder_to_copy = "target"
$init_file = "./init.ps1"

function Backup-Binary ( [String]$source, [String]$name, [String]$dest ) {

    $FullyQualifiedSourcePath = "$source\$name"

    if (Test-Path $FullyQualifiedSourcePath) {
        Write-Host "Backing up $FullyQualifiedSourcePath to the destination $dest"
        Copy-Item -Recurse -Force -Path $FullyQualifiedSourcePath -Destination $dest -ErrorAction Stop
    } else {
        Write-Output "Directory $FullyQualifiedSourcePath didn't exist. May be an initial deployment. Continuing with deployment."
    }

}

function Verify-Env ([String]$env_var) {
     $check = [Environment]::GetEnvironmentVariable($env_var)
     if ($check -eq $null) {
         Write-Error "$env_var environment variable doesn't exist. Exiting program."
         exit 1
     }
}

function Deploy-Local {

    # Checks that the given string exists as an Environment variable
    Verify-Env "DEPLOY_DRIVE"
    Verify-Env "CI_PROJECT_NAME"

    # C:\products or D:\products *most* of the time.
    $root = "$env:DEPLOY_DRIVE\products"
    $destination = "$root\$env:CI_PROJECT_NAME"
    $backup_loc = "$root\backup"

    # Removes prior binaries
    Backup-Binary -source $root -name $env:CI_PROJECT_NAME -dest $backup_loc

    # Copy binary locally since we're deploying to local machine.
    Copy-Item -Recurse -Force -Path $folder_to_copy -Destination $destination -ErrorAction Stop

    # Run the init script the developer has written
    Invoke-Expression "$init_file" -ErrorAction Stop

}

function Deploy-Remote {

    $session = New-PSSession -ComputerName $hostname

    $DEPLOY_DRIVE = Invoke-Command -Session $session -ScriptBlock { 
        [Environment]::GetEnvironmentVariable("DEPLOY_DRIVE")
    } -ErrorAction Stop

    $root = "$DEPLOY_DRIVE\products"
    $destination = "$root\$env:CI_PROJECT_NAME"
    $backup_loc = "$root\backup"

    Write-Host "$root = root $destination = destination $backup_loc = backup_location"

    Invoke-Command -Session $session -ScriptBlock ${function:Backup-Binary} -ArgumentList $root,$env:CI_PROJECT_NAME,$backup_loc -ErrorAction Stop

    Copy-Item -Recurse -Force -ToSession $session -Path $folder_to_copy -Destination $destination -ErrorAction Stop

    Invoke-Command -Session $session -FilePath $init_file -ErrorAction Stop

    Remove-PSSession $session -ErrorAction Stop

}

if ( $hostname.ToLower() -eq $env:COMPUTERNAME.ToLower() ) {

    Deploy-Local

} else {

    Deploy-Remote

}

deploy.ps1

Sub SapConfirm()

Application.ScreenUpdating = False
 'Sap automated confirmation
        Dim answer As Integer
        answer = MsgBox("You are about to confirm " & ActiveSheet.Range("B1") & " tray(s) of " & ActiveSheet.Range("A2").Value & vbNewLine & " SAP No. : " & ActiveSheet.Range("A1"), vbYesNo + vbQuestion, "Canceled")
        If answer = vbYes Then
                On Error GoTo safe_exit

    session.findById("wnd[0]/tbar[0]/okcd").Text = "z490"
    session.findById("wnd[0]/tbar[0]/btn[0]").press
    session.findById("wnd[0]/usr/ctxtMATNR-LOW").Text = ActiveSheet.Range("A1")
    session.findById("wnd[0]/usr/ctxtMATNR-LOW").SetFocus
    session.findById("wnd[0]/usr/ctxtMATNR-LOW").caretPosition = 6
    session.findById("wnd[0]/tbar[1]/btn[8]").press
    session.findById("wnd[1]").sendVKey 4
    session.findById("wnd[2]/usr/lbl[1,8]").SetFocus
    session.findById("wnd[2]/usr/lbl[1,8]").caretPosition = 4
    session.findById("wnd[2]").sendVKey 2
    session.findById("wnd[1]/tbar[0]/btn[0]").press
    session.findById("wnd[0]/tbar[1]/btn[5]").press
    session.findById("wnd[0]/usr/chk[1,3]").Selected = True
    session.findById("wnd[0]/tbar[1]/btn[5]").press
    session.findById("wnd[1]/usr/txtV_CONFIRMATION_QTY").Text = ActiveSheet.Range("B1")
    session.findById("wnd[1]").sendVKey 5
    session.findById("wnd[0]/tbar[0]/btn[15]").press
    session.findById("wnd[0]/tbar[0]/btn[15]").press
        'Here is where I would like the comment adding to take place
Else

     MsgBox ("Please login to SAP to enable confirmation process")
     Application.ScreenUpdating = True
safe_exit:
    MsgBox ("Please Login to SAP")
     Application.ScreenUpdating = True    
     End If
End Sub

1 个答案:

答案 0 :(得分:0)

这个答案来自provided link的@ mklement0(我在github上开始的一个问题)

  

@huffstler:

     

是的,但这与#2934的问题相同,只是在更大的命令的上下文中:

     
      
  • 当子目录。 a正在复制,$ b尚未存在,因此正在将内容直接复制到$ b(上述行为(a))。
  •   
  • 按时间分区。 b被复制,$ b已经存在,并且当不一致的情况发生时:b&#39; s内容被复制到$ b / b(上面的行为(b))。
  •   
     

因此,如果再次运行该命令,则会获得所需的行为。

     

鉴于当前行为,您可以按如下方式解决问题:

Get-ChildItem -Path $a |
ForEach-Object { New-Item -Force -Type Directory $b } { 
  Copy-Item -Recurse -Path $_.FullName -Destination $b
}
     

但更大的问题是这种不一致是否应该解决行为(a)或行为(b)。

     

您明确期望(a),但其他人可能会有不同的期望,基于xcopy。

     

坦率地说,我很困惑地发现与Unix世界中的Copy-Item -Recurse(cp -R)相同的不一致。

     

目前获得可预测行为的唯一方法是:

     
      
  • 确保目标目录。已存在。

  •   
  • 然后,取决于您是否需要行为(a)或(b):

  •   
  • 获取行为(a):明确定位源目录的内容:

         

    Copy-Item -Recurse -Force $a/* $b

         
        
    • 请注意需要-Force,这是确保隐藏项目也被复制所必需的。

    •   
    • 在Unix上使用cp,你可以更简单地只是$a/.,但这在PowerShell中不起作用。

    •   
  •   
  • 获取行为(b):无需采取进一步行动。

  •