1
votes

Comment référencer la sortie du "tuyau" précédent dans un pipeline Powershell?

J'ai écrit ce code pour obtenir un chemin de fichier (relatif):

$some_file_path = Get-ExecutingScriptDirectory | Join-Path -Path {{PREVIOUS STDOUT}} -ChildPath "foo.json" | Get-Content {{PREVIOUS STDOUT}} | Convert-FromJson {{PREVIOUS STDOUT}} | {{PREVIOUS STDOUT}}.data

Ceci a généré l'erreur:

$this_directory = Get-ExecutingScriptDirectory
$some_file_path = Join-Path -Path $this_directory -ChildPath "foo.json"

Cela indique à moi que la sortie de Get-ExecutingScriptDirectory est nulle - mais ce n'est pas le cas - quand j'écris le script comme ça, ça va:

Join-Path : Cannot bind argument to parameter 'Path' because it is null.
+ $some_file_path  = Get-ExecutingScriptDirectory | Join-Path -Path $_ -ChildPath "fo ...
+                                                     ~~
    + CategoryInfo          : InvalidData: (:) [Join-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCommand

Le problème est donc que $ _ est nul. Je m'attendrais à ce que $ _ fasse référence à la sortie standard du tube précédent. La documentation MSDN le suggère également, mais il semble alors se contredire immédiatement:

$ _ contient l'objet courant dans l'objet pipeline. Vous pouvez utiliser cette variable dans les commandes qui exécutent une action sur chaque objet ou sur des objets sélectionnés dans un pipeline.

Dans le contexte de mon code, $ _ semble être qualifié d '"objet actuel dans l'objet pipeline" - mais je ne l'utilise pas avec une commande qui effectue une action sur chaque objet ou sur des objets sélectionnés dans un pipeline.

$$ et $ ^ semblaient prometteurs, mais la documentation MSDN dit juste quelques vagues ici sur les jetons lexicaux. La documentation sur $ PSItem était tout aussi concise.

Ce que j'aimerais aimerait faire, c'est créer un gros pipeline:

function Get-ExecutingScriptDirectory() {
    return Split-Path $script:MyInvocation.MyCommand.Path  # returns this script's directory
}

$some_file_path = Get-ExecutingScriptDirectory | Join-Path -Path $_ -ChildPath "foo.json"

J'aimerais savoir où je me trompe à la fois sur le plan conceptuel et technique.


11 commentaires

si Get-ExecutingScriptDirectory renvoie une valeur qui est un chemin, vous pouvez la diriger directement vers Join-Path car le paramètre -Path accepte la valeur par pipeline -> Get-ExecutingScriptDirectory | Join-Path -ChildPath ... . Vous n'avez pas d'objet d'entrée courant $ _ ici car vous n'utilisez pas de commande avec un bloc de script capable d'en traiter un. Si vous utilisez Get-ExecutingScriptDirectory | Foreach-Object {} , vous aurez accès à $ _ dans le bloc de script Foreach et pourrez créer d'autres variables à votre guise.


@AdminOfThings ForEach-Object semble bizarre ici car la fonction précédente ( Get-ExecutingScriptDirectory ) ne produit qu'une seule chose (une chaîne). Je ne sais pas si c'est techniquement un "objet de type chaîne". De plus, je veux diriger toute la sortie de Get-ExecutingScriptDirectory vers un paramètre de Join-Path , et je n'ai pas vu de variable automatique qui semblait appropriée.


Déposez simplement -Path $ _ et cela fonctionnera.


@AdminOfThings Oui, mais les deux Get-ExecutingScriptDirectory | Join-Path -ChildPath "foo.json" | Get-Content $ _ et Get-ExecutingScriptDirectory | Join-Path -ChildPath "foo.json" | Get-Content ne fonctionne pas. Je ne sais pas pourquoi la sortie standard du pipeline précédent est automatiquement acheminée vers le paramètre -Path (par opposition au paramètre -ChildPath, ou autre).


En effet, Get-Content n'a pas de paramètre qui se lie par valeur à partir du pipeline. Vous devez soit diriger vers Foreach-Object et utiliser -Path $ _ , soit créer une propriété d'objet nommée Path avec votre chemin comme valeur avant rediriger vers Get-Content .


Utilisez simplement (Get-Content -Path (Get-ExecutingScriptDirectory | Join-Path -ChildPath "foo.json" | ConvertFrom-Json)). Data . Je ne comprends pas l'exigence de tuyauterie. Il est plus simple d'utiliser simplement plus d'une ligne.


@AdminOfThings Il n'y a aucune exigence de tuyauterie - la question était juste de faire avancer ma compréhension de la construction du langage. On dirait que vous dites que certaines fonctions ont des paramètres "adaptés aux tubes". Join-Path a -Path , Get-Content n'en a pas. Comment déterminez-vous généralement quelles fonctions ont des paramètres "pipe-friendly" dans l'écosystème Powershell? Actuellement, de mon point de vue, cela ressemble à des essais et des erreurs.


Vous ne pouvez utiliser $ _ qu'à l'intérieur d'un scriptblock {} . C'est assez fondamental. C'est dans la documentation quels paramètres peuvent être redirigés, certains n'ont besoin que par valeur, d'autres par nom de propriété.


@ js2010 Eh bien ... pas exactement. Voir docs.microsoft.com/en-us / powershell / module /… et docs.microsoft.com/en-us/powershell/module/… . Aucune distinction n'est faite au niveau de la partie paramètres de ces documents. Devez-vous à chaque fois explorer les exemples d'utilisation pour déterminer quels paramètres peuvent être acheminés?


Recherchez «accepter l'entrée du pipeline» dans ces documents.


@ js2010 Wow - yikes . Très bien, je suppose que cela répond à ma question. Merci.


3 Réponses :


1
votes

Utilisez simplement un bloc de code.

Get-Content (Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json")

La raison pour laquelle cela ne fonctionne pas dans ce code @ get-content est qu'il est évalué à faux.

function Get-ExecutingScriptDirectory() {
    return Split-Path $script:MyInvocation.MyCommand.Path  # returns this script's directory
}

if(Get-ExecutingScriptDirectory -eq $True){
    write-host This is true
}

Output: This is true

Un objet ne peut être transmis en utilisant $ _ ou $ psItem que s'il est évalué à $ True.

Voici pourquoi cela fonctionne dans le premier exemple.

Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json" | Get-Content {$_} and Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json" | Get-Content $_

Vous pouvez également utiliser Parethesis pour isoler vos objets dans le pipeline.

exemple:

function Get-ExecutingScriptDirectory() {
    return Split-Path $script:MyInvocation.MyCommand.Path  # returns this script's directory
}

$some_file_path = Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json"
$some_file_path 

read-host


1 commentaires

Bien sûr, mais cela ne m'aide pas à mieux comprendre pourquoi $ _ fonctionne, mais uniquement dans un contexte de bloc de script. En outre, l'approche de bloc de script échoue avec les deux Get-ExecutingScriptDirectory | Join-Path -Path {$ _} -ChildPath "foo.json" | Get-Content {$ _} et Get-ExecutingScriptDirectory | Join-Path -Path {$ _} -ChildPath "foo.json" | Get-Content $ _ .



1
votes

Vous pouvez faire ce que vous voulez en exécutant la commande ci-dessous:

"c:\temp" | Foreach-Object { $_ }
c:\temp

$filePath | Foreach-Object { $_ }

Path
----
c:\temp\test1\t.txt

$filePath | Foreach-Object { $_.Path }
c:\temp\test1\t.txt

Certaines commandes prennent en charge la liaison de paramètres de pipeline. Les options sont pipeline par valeur ou pipeline par propriété. Une bonne référence pour la liaison de paramètres est À propos des paramètres avancés des fonctions .

La recherche en ligne de la commande que vous allez utiliser donnera des informations sur la liaison des paramètres. Par exemple Join- Chemin , a une section de paramètres. Chaque paramètre aura une description comprenant le champ Accepter l'entrée du pipeline: . Pour qu'un paramètre accepte l'entrée de pipeline, il doit être True . Habituellement, il indiquera comment la valeur peut être transmise au pipeline ( ByPropertyName ou ByValue).

ByPropertyName indique que vous devez générer un objet contenant un nom de propriété correspondant au nom du paramètre. Ensuite, une fois que l'objet est acheminé, le paramètre se lie à la valeur du nom de propriété correspondant. Voir ci-dessous pour un exemple:

"c:\temp" | Join-Path -ChildPath "filepath" # c:\temp binds to `-Path`
c:\temp\filepath

ByValue indique que toute valeur transmise tentera de se lier à ce paramètre. S'il existe des différences de type au moment de la liaison, une erreur sera probablement générée. Voir ci-dessous par exemple:

$filePath = [pscustomobject]@{Path = 'c:\temp\test1\t.txt'}
$filePath

Path
----
c:\temp\test1\t.txt

$filePath.Path # This binds to -Path in Get-Content
c:\temp\test1\t.txt
$filepath | Get-Content 

Concernant $ _ , qui est synonyme de $ PSItem , est le objet d'entrée actuel dans un bloc de script. Vous le voyez généralement utilisé avec Foreach-Object et Where-Object . Si vous n'avez pas de bloc de script, vous ne pourrez pas utiliser $_ .

Vous pouvez techniquement tout diriger vers Foreach-Object code > ou Where-Object . Ensuite, l'objet de pipeline actuel sera représenté par $ _ . Vous n'avez pas besoin d'une collection car un seul élément peut être inséré. Voir ci-dessous:

(Get-Content -Path (Get-ExecutingScriptDirectory | Join-Path -ChildPath "foo.json" | ConvertFrom-Json)).data


0 commentaires

1
votes

Voici un exemple simple. Recherchez «accepter l'entrée du pipeline» dans la documentation. L'utilisation d'un scriptblock avec le paramètre -path de get-content est un peu avancé. La plupart des gens utilisent à la place un objet foreach. Cela fonctionne car -path accepte l'entrée de pipeline, mais uniquement par nom de propriété. Le paramètre -path de join-path peut être sur le tube par valeur, donc c'est plus facile. Cela vous sera très utile une fois que vous l'aurez compris. Il est peut-être plus facile d'essayer des choses parfois.

'.\' | Join-Path -ChildPath foo.json | foreach { Get-Content $_ } | ConvertFrom-Json

Ou avec foreach, qui est l'abréviation de foreach-object dans ce cas. Mais $ _ doit toujours être entre les accolades.

echo '"hi"' > foo.json

'.\' | Join-Path -ChildPath foo.json | Get-Content -Path { $_ } | ConvertFrom-Json

hi


0 commentaires