foreach: an empty pipe element is not allowed

I can’t sort the output of one form of foreach, but I can another?

PS C:\> 3,1,2 | foreach { $_ } | sort                                 

1
2
3


PS C:\> foreach ($a in 3,1,2) { $a } | sort                           

At line:1 char:30
+ foreach ($a in 3,1,2) { $a } | sort
+                              ~
An empty pipe element is not allowed.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordEx 
   ception
    + FullyQualifiedErrorId : EmptyPipeElement


PS C:\> $b = foreach ($a in 3,1,2) { $a }                             
PS C:\> $b | sort                                                     

1
2
3
 

In the second case you’re using a language construct, not a command. It’s output isn’t transmitted down the pipeline in the way you’re expecting and so Sort has no input.

These are not different “forms” of the same thing. They’re two deeply different things that have the same name. Unfortunately.

How come I can save the output of the foreach keyword to a variable?

As Don has mentioned, the foreach’s you are using are completely different.

PS C:\> 3,1,2 | foreach { $_ } | sort

The foreach in the above is actually “ForEach-Object”, which is a cmdlet. The output of the cmdlet is sent to the pipeline.
https://msdn.microsoft.com/en-us/powershell/reference/6/microsoft.powershell.core/foreach-object

PS C:\> foreach ($a in 3,1,2) { $a } | sort

The foreach in this is a language construct. The construct is not a cmdlet, and does not return a value to the pipeline. Instead, the end of the pipeline is reached inside of script block and the value it sent to the default output.

You can find more information on the differences between these two usages in the Microsoft Documentation of about_ForEach
https://msdn.microsoft.com/powershell/reference/5.1/Microsoft.PowerShell.Core/about/about_ForEach