Reputation: 7443
I wrote this code to get some (relative) file path:
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"
This threw the error:
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
This indicates to me that the output of Get-ExecutingScriptDirectory
is null - but it isn't - when I write the script out like this, it's fine:
$this_directory = Get-ExecutingScriptDirectory
$some_file_path = Join-Path -Path $this_directory -ChildPath "foo.json"
So the problem is that $_
is null. I would expect $_
to be reference to the previous pipe's standard output. The MSDN documentation also suggests this, but then it seems to immediately contradict itself:
$_ contains the current object in the pipeline object. You can use this variable in commands that perform an action on every object or on selected objects in a pipeline.
In the context of my code, $_
seems to qualify as the "current object in the pipeline object" - but I am not using this with a command that performs an action on every object or on selected objects in a pipeline.
$$
and $^
looked promising, but the MSDN documentation just says a few vague things here about lexical tokens. The documentation on $PSItem
was equally terse.
What I would actually like to do is create a big pipeline:
$some_file_path = Get-ExecutingScriptDirectory | Join-Path -Path {{PREVIOUS STDOUT}} -ChildPath "foo.json" | Get-Content {{PREVIOUS STDOUT}} | Convert-FromJson {{PREVIOUS STDOUT}} | {{PREVIOUS STDOUT}}.data
I'd like to know where I'm going wrong both on a conceptual and a technical level.
Upvotes: 3
Views: 11560
Reputation: 1351
Just use a code block.
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
The reason it doesn't work in this code @ get-content is because it's evaluating to false there.
Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json" | Get-Content {$_} and Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json" | Get-Content $_
An object can only be passed on using $_ or $psItem if it evaluates to $True.
Here is why it works in the first example.
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
You can use Parethesis to isolate your objects in the pipeline also.
example:
Get-Content (Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json")
"|" deals with the object to the left of it, so doing get-content | get-content | doesn't make sense to the script. You will need to separate it into multiple commands with a semi-colon if you're doing something like that. Or use a "Foreach" cmdlet.
gc (Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json");gc (Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "bar.json")
Upvotes: 1
Reputation: 27453
Here's a simple example. Search for "accept pipeline input" in the docs. Using a scriptblock with get-content's -path parameter is a little advanced. Most people use a foreach-object instead. It works because -path accepts pipeline input, but only by property name. join-path's -path paramaeter can be over the pipe by value, so it's easier. This will come in handy a lot once you understand it. Maybe it's easier just to try things sometimes.
echo '"hi"' > foo.json
'.\' | Join-Path -ChildPath foo.json | Get-Content -Path { $_ } | ConvertFrom-Json
hi
Or with foreach, which is short for foreach-object in this case. But $_ must always be inside the curly braces.
'.\' | Join-Path -ChildPath foo.json | foreach { Get-Content $_ } | ConvertFrom-Json
Upvotes: 2
Reputation: 25001
You can do what you want by executing the command below:
(Get-Content -Path (Get-ExecutingScriptDirectory | Join-Path -ChildPath "foo.json" | ConvertFrom-Json)).data
Some commands support pipeline parameter binding. The options are pipeline by value or pipeline by property. A good reference for parameter binding is About Functions Advanced Parameters.
Searching online for the command you are going to use will yield parameter binding information. For example Join-Path, has a parameters section. Each parameter will have a description including the field Accept pipeline input:
. For a parameter to accept pipeline input, this must be True
. Usually, it will say how the value can be passed into the pipeline (ByPropertyName
or ByValue
).
ByPropertyName
indicates that you must output an object that contains a property name that matches the parameter name. Then once the object is piped, the parameter will bind to the matching property name's value. See below for an example:
$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
ByValue
indicates that any value piped in will attempt to bind to that parameter. If there are type discrepancies at the binding time, an error will likely be thrown. See below for example:
"c:\temp" | Join-Path -ChildPath "filepath" # c:\temp binds to `-Path`
c:\temp\filepath
Regarding $_
, which is synonymous with $PSItem
, is the current input object in a script block. You typically see this used with Foreach-Object
and Where-Object
. If you don't have a script block, you won't be able to use $_
.
You can technically pipe anything into Foreach-Object
or Where-Object
. Then the current pipeline object will be represented by $_
. You don't need a collection as a single item can be piped in. See below:
"c:\temp" | Foreach-Object { $_ }
c:\temp
$filePath | Foreach-Object { $_ }
Path
----
c:\temp\test1\t.txt
$filePath | Foreach-Object { $_.Path }
c:\temp\test1\t.txt
Upvotes: 1