function read_services {
$entry
$entries = @()
foreach ($line in get-content /windows/system32/drivers/etc/services) {
($name, $port, $description) = $line -split ' +'
$entry && ($entries += $entry)
$entry = [ordered]@{
'name' = $name;
'port' = $port;
'comment' = $description;
};
}
}
$services = read_services
write-host $services.count
Output:
41903
With the &&
construct changed to an if statement, it works correctly:
function read_services {
$entry
$entries = @()
foreach ($line in get-content /windows/system32/drivers/etc/services) {
($name, $port, $description) = $line -split ' +'
if ($entry) { $entries += $entry }
$entry = [ordered]@{
'name' = $name;
'port' = $port;
'comment' = $description;
};
}
return $entries
}
$services = read_services
write-host $services.count
Output:
287
Name Value
---- -----
PSVersion 7.0.0-rc.2
PSEdition Core
GitCommitId 7.0.0-rc.2
OS Microsoft Windows 10.0.19551
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Using an _expression_ such as $entry
as the LHS of &&
is pointless, because &&
and ||
act on the _success status_ of the LHS, and an expression's success status is _always_ $true
(unless a statement-terminating error occurs, in which case the entire statement is terminated).
PS> $null && 'yes'
yes
Use &&
and ||
only with _commands_, where the implied $?
value - not the command's _output_ - determines the success status.
I think @rjmholt was the one doing the work with &&
and ||
if I'm not mistaken? 馃 (Feel free to correct me if my memory is incorrect!)
Essentially, &&
in PS does _not_ work like it does in C#. It instead behaves somewhat more closely to how it behaves in Bash and Bash-like shells, where it acts on the _perceived success or failure of the prior command_.
&&
only triggers the following statement if the prior statement succeeds. ||
is the exact opposite. Whether or not a statement _succeeds_ is typically down to whether or not it generates an error of some kind, and nothing more.
You can do something similar to what you seem to be looking to do with PS's logical -and
operator to a similar effect:
$entry -and ($entries += $entry)
However, do note that -and
is a logical expression and will always return true or false. If you want this to behave like the if statement, you'll need to also capture or redirect the output:
$entry -and ($entries += $entry) > $null
# or
$null = $entry -and ($entries += $entry)
A few asides, @rkitover:
$entry
by itself doesn't declare a variable; it outputs the value of a non-existent $entry
variable, which defaults to $null
.
No need for (...)
around destructuring assingments.
-split
splits by nonempty runs of whitespace (trimming and leading whitespace too)Thus:
$name, $port, $description = -split $line
+=
is inefficient (a _new_ array is created in each iteration) and verbose; simply treat the entire foreach
loop as an expression and let PowerShell collect the results in an array for you, which bypasses your initial problem:[array] $entries = foreach ($line in get-content /windows/system32/drivers/etc/services) {
$name, $port, $description = -split $line
# Construct and output
[ordered] @{
name = $name
port = $port
comment = $description
}
}
}
@vexx32
Whether or not a statement succeeds is typically down to whether or not it generates an error of some kind, and nothing more.
Essentially, it comes down to the value of $?
after execution of the _command_ (or, pointlessly, _expression_; due to the current grammar it can't be a whole _statement_), which means that $?
is $false
if:
with a PowerShell command: if it writes to the error stream, which means emitting at least one _non-terminating error_.
with an external program: if it reports a _nonzero exit code_ (as reflected in $LASTEXITCODE
).
2>
redirection incorrectly sets $?
to $false
in the presence of stderr output even if $LASTEXITCODE
is 0
.with PS's logical -and operator
I suggest not recommending the use of -and
, because it serves a fundamentally different purpose.
An if
statement or ternary conditional is the appropriate construct to use for _expressions_ and also Test-*
commands (which signal their success by _outputting_ a Boolean).
Yes $x && $y
is not the same as $x; if ($x) { $y }
. It's instead the same as $x; if ($?) { $y }
.
&&
and ||
are designed to encode command success, not expression value. In that case, you should use if
or a ternary (or add your 馃憤 to this proposal).
I'm sure eventually you guys will make all of this more intuitive, thank you for all the great info! I'm very new to powershell.
The thing that actually confused me here is that powershell can return data from multiple places and it will all be streamed together. I have never seen anything like that before. That's actually pretty awesome.
When I see differences from UNIX for some things in powershell, usually powershell makes the correct design choice. Not to say that everything is not controversial.
If it helps any @rkitover, you might want to check out https://aka.ms/pskoans to get familiar with some of the nuance that can trip you up from time to time in PS 馃檪
Also, I highly recommend reading through the about_*
help docs in PS -- they're all available from Get-Help
or the online documentation. There's a _ton_ of good info in those.
GitHubA simple, fun, and interactive way to learn the PowerShell language through Pester unit testing. - vexx32/PSKoans
A final note on the pipeline-chain operators, &&
and ||
(see about_Pipeline_Chain_Operators
): They were borrowed from the world of POSIX-like shells (e.g., Bash) and are primarily useful for chaining _external programs_ (e.g., git
) that reliably reflect their overall success or failure though their _process exit code_.
The thing that actually confused me here is that powershell can return data from multiple places and it will all be streamed together. I have never seen anything like that before. That's actually pretty awesome.
It is indeed awesome if you learn to harness it to your advantage.
PowerShell balances many worlds deftly (mostly - interaction with external programs is a perennial trouble spot), but the challenge is that if you come from only _one_ similar world, such as Unix shells or traditional programming languages, and expect everything to work the same, pain ensues.
While you can limp along with only ever using return
and echo 'foo'
, ..., it really pays to familiarize yourself with the fundamentals of PowerShell, which is what @vexx32's PS Koans project helps with (plus the about_*
topics he mentions).
Part of learning the fundamentals is learning how to use the help system (Get-Help
) effectively, so you can perform targeted lookups not just for whole commands, but also for examples and individual parameters.
There is no support (yet) for looking up _operators_ directly via Get-Help
- see the suggestion in #11339, which also points to a helper command, Show-OperatorHelp
, which would allow you to run Show-OperatorHelp '&&'
, for instance.
This issue has been marked as answered and has not had any activity for 1 day. It has been closed for housekeeping purposes.
Most helpful comment
Yes
$x && $y
is not the same as$x; if ($x) { $y }
. It's instead the same as$x; if ($?) { $y }
.&&
and||
are designed to encode command success, not expression value. In that case, you should useif
or a ternary (or add your 馃憤 to this proposal).