web-dev-qa-db-fra.com

L'exécution de Get-ChildItem sur le chemin UNC fonctionne dans Powershell mais pas dans Powershell exécuté dans un fichier de commandes

J'écris un fichier batch qui exécute un script Powershell qui à un moment donné boucle des éléments avec des chemins UNC comme attributs et utilise Get-ChildItem sur ces chemins. Dans une version minimale, c'est ce qui se passe dans mes 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
}

Le problème que je rencontre est que lorsque j'exécute Master.bat , il échoue à Get-ChildItem avec une erreur du type

get-childitem : Cannot find path '\\remote-server\foothing' because it does not exist.

Cependant, cela semble fonctionner parfaitement si j'exécute le fichier Slave.ps1 directement à l'aide de Powershell. Pourquoi cela peut-il se produire uniquement lorsque le fichier Master.bat est exécuté?

Ce que j'ai essayé

  • Ajout des chemins UNC avec FileSystem:: avec les fournisseurs http://powershell.org/wp/2014/02/20/powershell-gotcha-unc-paths-and-providers/
  • S'assurer qu'il n'y a pas de caractères étranges dans les chemins réels
  • En utilisant le -literalPath paramètre au lieu du simple -path paramètre pour Get-ChildItem
  • Fonctionnement Get-ChildItem \\remote-server\foothing dans PowerShell et réussissant à vérifier la connexion au serveur distant
22
Jon Chan

J'ai rencontré ce problème lors de l'exécution de scripts faisant référence à des chemins UNC - mais l'erreur ne se produit que lorsque la racine du script est définie sur un emplacement autre que le système de fichiers. par exemple. PS SQLSEVER \

Donc, ce qui suit échoue avec la même erreur:

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
}

Donc, ma résolution était de m'assurer que l'invite PS était retournée à un emplacement du système de fichiers avant d'exécuter ce code. par exemple.

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
}

J'espère que cela aide - je serais très heureux de la prime car c'est ma première réponse sur le débordement de pile. P.S. J'ai oublié d'ajouter - la racine d'invite de commande PS peut être définie par des modules chargés automatiquement dans la configuration de votre machine. Je vérifierais avec Get-Location pour voir si vous exécutez réellement à partir d'un emplacement non FileSystem.

40
Rory

réponse de Rory fournit une solution de contournement efficace, mais il existe une solution qui ne nécessite pas de changer l'emplacement actuel en un emplacement de fournisseur FileSystem en premier :

Préfixez vos chemins UNC avec FileSystem:: pour vous assurer qu'ils sont reconnus correctement, quel que soit l'emplacement actuel:

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

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

Alternativement, voici un Tweak à la réponse de Rory pour éviter de changer la session d'emplacement actuelle de manière globale (pour conserver l'emplacement actuel), en utilisant Push-Location et 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
}

Informations générales facultatives

Cet excellent article de blog explique le problème sous-jacent (emphase ajoutée):

PowerShell ne reconnaît pas les [chemins UNC] comme "enracinés" car ils ne sont pas sur un PSDrive; en tant que tel, , quel que soit le fournisseur associé à l'emplacement actuel de PowerShell, il tentera de les gérer .

Ajout du préfixe FileSystem:: identifie sans ambiguïté le chemin comme étant un chemin de fournisseur FileSystem, quel que soit le fournisseur sous-jacent à l'emplacement actuel.

12
mklement0

J'ai lu ailleurs sur le Push-Location et Pop-Location commandes pour contrer ce genre de problème - J'ai atterri sur votre question en testant manuellement, étape par étape, une nouvelle routine où le script a Push/pop, mais j'ai oublié de les faire sur ma fenêtre PS. Après avoir vérifié la réponse de @ Rory, j'ai remarqué que j'étais sur PS SQLServer:\au lieu de PS C:\Prompt.

Donc, une façon de l'utiliser sur votre script "esclave" serait:

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

Pensé à ajouter le Push/Pop avant et après le # Do things car il semble que ce sont ces choses qui changent l'emplacement.

0
João Ciocca