问题
I am writing a batch file that executes a Powershell script that at one point loops items with UNC paths as attributes and uses Get-ChildItem
on those paths. In a minimal version, this is what is happening in my scripts:
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
}
The problem I'm running into is that when I run Master.bat, it fails at Get-ChildItem
with an error along the lines of
get-childitem : Cannot find path '\\remote-server\foothing' because it does not exist.
However, it seems to work perfectly fine if I run the Slave.ps1 file directly using Powershell. Why might this be happening only when the Master.bat file is run?
Things I have tried
- Prepending the UNC paths with
FileSystem::
with providers http://powershell.org/wp/2014/02/20/powershell-gotcha-unc-paths-and-providers/ - Making sure there are no strange characters in the actual paths
- Using the
-literalPath
parameter instead of the plain-path
parameter forGet-ChildItem
- Running
Get-ChildItem \\remote-server\foothing
in PowerShell and succeeding to verify connection to the remote server
回答1:
I have found this issue when running scripts referring to UNC paths - but the error only occurs when the root of the script is set to a non file system location. e.g. PS SQLSEVER\
So the following fails with the same error:
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
}
So my resolution was to ensure that the PS prompt was returned to a file system location before executing this code. e.g.
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
}
I hope this helps - I would be very happy with the bounty as this is my first answer on stack overflow. P.S. I forgot to add - the PS command prompt root may be set by auto loaded modules in the configuration of your machine. I would check with Get-Location to see if you are actually executng from a non FileSystem location.
回答2:
Rory's answer provides an effective workaround, but there's a solution that doesn't require changing the current location to a FileSystem provider location first:
Prefix your UNC paths with FileSystem::
to ensure that they are recognized correctly, irrespective of the current location:
$foo = @{Name = "Foo"}
$foo.Path = "FileSystem::\\remote-server\foothing"
$bar = @{Name = "Bar"}
$bar.Path = "FileSystem::\\remote-server\barthing"
Alternatively, here is a tweak to Rory's answer to avoid changing the current location session-globally (to preserve whatever the current location is), using Push-Location
and Pop-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
}
Optional background information
This excellent blog post explains the underlying problem (emphasis added):
PowerShell doesn't recognize [UNC paths] as "rooted" because they're not on a PSDrive; as such, whatever provider is associated with PowerShell's current location will attempt to handle them.
Adding prefix FileSystem::
unambiguously identifies the path as being a FileSystem provider path, irrespective of the provider underlying the current location.
回答3:
I read somewhere else about the Push-Location and Pop-Location commands to counter this kind of problem - I landed on your question while manually, step-by-step, testing a new routine where the script has push/pop, but I forgot to do them on my PS window. After checking @Rory's answer I noticed I was on PS SQLServer:\ instead of PS C:\ prompt.
So a way to use this on your "slave" script would be:
$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
}
Thought of adding the Push/Pop before and after the # Do things
because it seems that it's those things that change the location.
来源:https://stackoverflow.com/questions/23574653/running-get-childitem-on-unc-path-works-in-powershell-but-not-in-powershell-run