在PowerShell中运行Get-ChildItem对于UNC路径有效,但在批处理文件中运行的PowerShell却无效。

26

我正在编写一个批处理文件,该文件执行一个PowerShell脚本,在其中使用UNC路径作为属性循环项,并在这些路径上使用Get-ChildItem。 在我的脚本中,以下是正在发生的最小版本:

Master.bat

powershell -ExecutionPolicy ByPass -File "Slave.ps1"

Slave.ps1

$foo = @{Name = "Foo"}
$foo.Path = "\\remote-server\foothing"

$bar = @{Name = "Bar"}
$bar.Path = "\\remote-server\barthing"

@( $foo, $bar ) | ForEach-Object {
    $item = Get-ChildItem $_.Path
    # Do things with item
}

我遇到的问题是,当我运行Master.bat时,在Get-ChildItem处失败,并显示类似以下的错误信息。
get-childitem : Cannot find path '\\remote-server\foothing' because it does not exist.

然而,如果我直接在PowerShell中运行Slave.ps1文件,则似乎完美地工作。为什么只有在运行Master.bat文件时才会出现这种情况?

我尝试过的方法


1
批处理文件是如何运行的?您只是双击运行它吗?它是由任务计划程序运行的吗?当批处理文件运行时,是您的凭据在运行脚本吗? - TheMadTechnician
当以管理员身份运行时,它是否提示凭据?如果您只是运行批处理文件而不是以管理员身份运行它,是否会出现相同的错误? - TheMadTechnician
无论是以管理员身份运行还是普通用户身份运行,都没有区别。错误仍然会出现。 - Jon Chan
这在我的电脑上运行良好,即使使用PS V2也是如此 powershell -version 2 -ExecutionPolicy ByPass -File "c:\temp\Slave.ps1" - Loïc MICHEL
这对我有效。检查路径/提供程序。 - James Woolfenden
显示剩余2条评论
3个回答

48

我在运行引用UNC路径的脚本时遇到了这个问题 - 但只有当脚本的根目录设置为非文件系统位置时才会出现错误。例如,PS SQLSERVER\

因此,以下内容也将以相同的错误失败:

cd env:
$foo = @{Name = "Foo"}
$foo.Path = "\\remote-server\foothing"

$bar = @{Name = "Bar"}
$bar.Path = "\\remote-server\barthing"

@( $foo, $bar ) | ForEach-Object {
    $item = Get-ChildItem $_.Path
    # Do things with item
     Write-Host $item
}
所以我的解决方案是确保在执行这段代码之前将PS提示符返回到文件系统位置,例如。
cd env:
$foo = @{Name = "Foo"}
$foo.Path = "\\remote-server\foothing"

$bar = @{Name = "Bar"}
$bar.Path = "\\remote-server\barthing"

cd c: #THIS IS THE CRITICAL LINE
@( $foo, $bar ) | ForEach-Object {
    $item = Get-ChildItem $_.Path
    # Do things with item
     Write-Host $item
}

我希望这可以帮到你 - 如果这是我在堆栈溢出上的第一个答案,我会非常高兴得到奖励。 附言:我忘记了添加 - 可能由于自动加载模块在您的计算机配置中设置了PS命令提示符根。 我建议使用Get-Location命令检查是否实际执行了非FileSystem位置。


2
+1 您,先生,是个天才。我花了很多时间试图弄清楚为什么这对我不起作用!!非常感谢。 - Steve365
2
谢谢,谢谢,谢谢!!我遇到了同样的问题,脚本失败并将我踢回PS SQLSEVER. - Chris
2
非常好,我使用cd $pwd以避免破坏其他路径,但是在没有您的建议的情况下很难找到这个方法! - Rogier
七年后,你仍在帮助! - Joe B

14

Rory的回答 提供了一种有效的解决方法,但是还有一种不需要先将当前位置更改为文件系统提供程序位置的方案:

在UNC路径前面添加FileSystem::前缀可以确保它们被正确识别,无论当前位置如何:

$foo = @{
   Name = "Foo"
   Path = "FileSystem::\\remote-server\foothing"
}

$bar = @{
   Name = "Bar"
   Path = "FileSystem::\\remote-server\barthing"
}

相反,这里是对Rory答案的微调,以避免全局更改当前位置会话(以保留当前位置),使用Push-LocationPop-Location

try {
  # Switch to the *filesystem provider's* current location, whatever it is.
  Push-Location (Get-Location -PSProvider FileSystem)

  # Process the paths.
  $foo, $bar | ForEach-Object {
      $item = Get-ChildItem $_.Path
      # Do things with item
  }
} finally {
   # Restore the previous location.
   Pop-Location
}

可选的背景信息

这篇精彩的博客文章解释了潜在的问题(强调添加):

PowerShell不会将[UNC路径]识别为“根”,因为它们不在PSDrive上;因此,与PowerShell当前位置相关联的任何提供程序都将尝试处理它们

添加前缀FileSystem::可以明确地将路径标识为FileSystem提供程序路径,而不管当前位置下的提供程序是什么。


1
我已经苦苦挣扎了一段时间,你的解决方案对我非常有效。尽管我之前使用[System.IO.Directory]::Exists($path)检查过该行,但在Get-ChildItem -Path $path时仍然出现错误。加上FileSystem::后问题得到了解决。 - kerzek
很高兴听到这个好消息,@kerzek。是的,使用[System.IO.Directory]::Exists($path)总是有效的,因为.NET只知道文件系统路径,不像PowerShell具有多个驱动器提供程序。 - mklement0

0

我在其他地方读到了一些关于Push-LocationPop-Location命令的内容,以解决这种问题。当手动、逐步测试新程序时,脚本具有推送/弹出功能,但我忘记在我的PS窗口上执行它们。在检查@Rory的答案后,我注意到我在PS SQLServer:\而不是PS C:\提示符下。

因此,在您的“从属”脚本中使用它的方法是:

$foo = @{Name = "Foo"}
$foo.Path = "\\remote-server\foothing"

$bar = @{Name = "Bar"}
$bar.Path = "\\remote-server\barthing"

@( $foo, $bar ) | ForEach-Object {
    $item = Get-ChildItem $_.Path
    Push-Location
    # Do things with item
    Pop-Location
}

考虑在# 做事情之前和之后添加Push/Pop,因为似乎正是这些事情改变了位置。


使用 Push-Location / Pop-Location暂时 切换到文件系统位置是个好主意,但您的解决方案行不通,因为 (a) 您的 Push-Location 命令出现在 Get-ChildItem 之后,而这里出现了错误;(b) 即使您改变了顺序,没有参数的 Push-Location 只是将当前位置推入被记住的位置栈,它不会切换到 文件系统 位置,这才是所需的。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接