web-dev-qa-db-ja.com

UNCパスでGet-ChildItemを実行すると、Powershellで機能しますが、Powershellではバッチファイルで実行できません

ある時点で属性としてUNCパスを持つアイテムをループし、それらのパスでGet-ChildItemを使用するPowershellスクリプトを実行するバッチファイルを書いています。最小バージョンでは、これは私のスクリプトで起こっていることです:

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ファイルが実行されている場合にのみ、これが発生するのはなぜですか?

私が試したもの

  • プロバイダーを使用してFileSystem::をUNCパスの先頭に追加 http://powershell.org/wp/2014/02/20/powershell-gotcha-unc-paths-and-providers/
  • 実際のパスに奇妙な文字がないことを確認する
  • -literalPathのプレーン-pathパラメーターの代わりにGet-ChildItemパラメーターを使用する
  • PowerShellでGet-ChildItem \\remote-server\foothingを実行し、リモートサーバーへの接続の検証に成功する
22
Jon Chan

UNCパスを参照するスクリプトを実行するとこの問題が見つかりましたが、スクリプトのルートがファイルシステム以外の場所に設定されている場合にのみエラーが発生します。例えばPS SQLSEVER \

したがって、以下は同じエラーで失敗します。

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以外の場所から実際に実行されているかどうかを確認します。

40
Rory

Roryの答え は効果的な回避策を提供しますが、現在の場所を最初にFileSystemプロバイダーの場所に変更する必要のないソリューション:があります。

NCパスの前にFileSystem::を付けて、現在の場所に関係なく、正しく認識されるようにします。

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

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

あるいは、_-Push-LocationPop-Locationを使用して、Roryの答えを微調整から現在の場所のセッションをグローバルに変更しない(現在の場所を保持するため)です:

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はPSUNC上にないため、[UNCパス]を「ルート化された」ものとして認識しません。そのため、PowerShellの現在の場所に関連付けられているプロバイダーはすべて、それらを処理しようとします

プレフィックスFileSystem::を追加すると、現在の場所の基になるプロバイダーに関係なく、パスがFileSystemプロバイダーパスであることが明確に識別されます。

12
mklement0

この種の問題に対処する Push-Location および Pop-Location コマンドについて他の場所を読みました-手動でステップバイステップで質問に行きました-step、スクリプトにプッシュ/ポップがある新しいルーチンをテストしますが、PSウィンドウでそれらを実行するのを忘れていました。 @Roryの答えを確認した後、PS C:\プロンプトではなくPS SQLServer:\にいることに気付きました。

したがって、これを「スレーブ」スクリプトで使用する方法は次のとおりです。

$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
}

# Do thingsの前後にプッシュ/ポップを追加することの考えは、場所を変更するのはこうしたものだと思われるためです。

0
João Ciocca