1

Total powershell noob and trying to go into a directory. I know it is the last directory (alphabetically) and I can get that with

Get-ChildItem -Directory | Select-Object -Last 1

I then want to Set-Location to that but I can't figure out how to call it with the output of the above.

I was trying something like:

Get-ChildItem -Directory | Select-Object -Last 1

Set-Variable -Name "Dir" -Value { Get-ChildItem -Directory | Select-Object -Last 1 }

Set-Location -Path $Dir

but that doesn't work at all. Any pointers?

Dominic Shaw
  • 210
  • 2
  • 10
  • 1
    What's wrong with using the pipe? `Get-ChildItem -Directory | Select-Object -Last 1 | Set-Location` or better `Get-ChildItem -Directory | Select-Object -Last 1 | Push-Location ` – Olaf Aug 27 '20 at 11:26
  • 1
    I agree with @Olaf on using the pipeline but I just figured I'd mention that `Set-Variable -Name "Dir" -Value $(Get-ChildItem -Directory | Select-Object -Last 1)` would work in your example. The way you have it you're actually saving the actual scriptblock to the variable rather than the result of running `Get-ChildItem`. Just for reference, the ScriptBlock can be invoked and used `Set-Location -Path $($Dir.Invoke())` but again, go with the pipeline solution Olaf mentioned. – notjustme Aug 27 '20 at 11:47
  • Fantastic thanks very much that's what I need. I have never used PS before, I wasn't sure what a pipe did... I'm still not 100% sure! I will have a read. – Dominic Shaw Aug 27 '20 at 11:47
  • @notjustme that is really helpful thanks - explains all the things I was missing in my attempt – Dominic Shaw Aug 27 '20 at 11:50

1 Answers1

3

There's good information in the comments, let me try to summarize and complement them:

  • { ... } is a script block literal in PowerShell: a reusable piece of code to be invoked later with &, the call operator; that is, what's inside the curly braces is not executed right away.

  • Given that you want the execute and return the output from a command as the argument to another command, use (), the grouping operator[1].

Set-Variable -Name Dir -Value (Get-ChildItem -Directory | Select-Object -Last 1)

However, there is rarely a need to use the Set-Variable cmdlet, given that simple assignments - $Dir = ... work too, in which case you don't even need the parentheses:

$Dir = Get-ChildItem -Directory | Select-Object -Last 1

Of course, just like you can pass an (...) expression to Set-Variable, you can pass it to Set-Location directly, in which case you don't need an intermediate variable at all (parameter -Path is positionally implied):

Set-Location (Get-ChildItem -Directory | Select-Object -Last 1)

You can make this more concise by directly indexing the Get-ChildItem call's output[2]; [-1] refers to the last array element (output object):

Set-Location (Get-ChildItem -Directory)[-1]

The alternative is to use the pipeline (see about_Pipelines):

Get-ChildItem -Directory | Select-Object -Last 1 | Set-Location

Note that there are subtle differences between the last 3 commands in the event that no subdirectories exist in the current directory (meaning that Get-ChildItem -Directory produces no output):

  • Set-Location (Get-ChildItem -Directory | Select-Object -Last 1) will report an error, because you're then effectively passing $null as the -Path argument.

  • Set-Location (Get-ChildItem -Directory)[-1] will also report an error, because you cannot apply an index to a $null value.

  • Get-ChildItem -Directory | Select-Object -Last 1 | Set-Location, by contrast, will be a no-op, because Set-Location effectively won't be invoked at all, due to not receiving input via the pipeline.

If not having subdirectories is an unexpected condition, you can force the script to abort by adding -ErrorAction Stop to one of the first two commands - see about_CommonParameters.


[1] While $(...), the subexpression operator, works too, it's usually not necessary - see this answer.

[2] Note that doing so means that all output from the Get-ChildItem call is then collected in memory first, but that is unlikely to be a problem in this case. See this answer for more information.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Really helpful and the extra explanation it hugely appreciated, thanks very much – Dominic Shaw Aug 27 '20 at 12:32
  • Glad to hear it, @DominicShaw; my pleasure. Please also see the footnote that I've just added re streaming behavior in the pipeline vs. up-front collection of all outputs via `(...)` – mklement0 Aug 27 '20 at 14:02
  • Good summarization and explaination. That footnote reference alone is an instant +1 considering the topic. :) – notjustme Aug 27 '20 at 14:08