Short question: anyone has detailed information on -RemainingScripts parameter of ForEach-Object?
Long question:
I just started
The source code seems to agree. From powershell on github I was able to find the following relevant pieces of source for the ForEach-Object
cmdlet.
The /* ... */
comments are mine, and I've otherwise somewhat altered the code, so refer the the actual source for proper reference.
The first part is just how the parameters are initially read. The important thing to note is that all
s are put into the same array, save for -End
. The -End
parameter is handled specially, probably because it's easier to do it the way they did (i.e. it's easy to make sure that something is the first item in the list, just only append to it, but if you do that ensuring something is the last item in the list requires you know you won't append anything else to it).
private List _scripts = new List();
public ScriptBlock Begin {
/* insert -Begin arg to beginning of _scripts */
set { _scripts.Insert(0, value); }
}
ScriptBlock[] Process {
/* append -Process args to _scripts */ }
set {
if (value == null) { _scripts.Add(null); }
else { _scripts.AddRange(value); }
}
}
private ScriptBlock _endScript;
private bool _setEndScript;
ScriptBlock End {
set {
_endScript = value;
_setEndScript = true;
}
}
public ScriptBlock[] RemainingScripts {
set {
if (value == null) { _scripts.Add(null); }
else { _scripts.AddRange(value); }
}
}
Here are the functions which invoke the
s in the _script
data member. Basically, if there is more than one
in _scripts
, _start
gets set to 1. If there is at least three, and -End
wasn't explicitly set, _end
gets set to _scripts[_scripts.Count - 1]
. Then _script[0]
gets invoked, the stream is opened up for _script[1.._end-1]
, and then _endScript
is invoked (either _script[_end]
or -End
).
private int _start, _end;
private void InitScriptBlockParameterSet() {
// Calculate the start and end indexes for the processRecord script blocks
_end = _scripts.Count;
_start = _scripts.Count > 1 ? 1 : 0;
// and set the end script if it wasnt explicitly set with a named parameter.
if (!_setEndScript) {
if (_scripts.Count > 2) {
_end = _scripts.Count - 1;
_endScript = _scripts[_end];
}
}
// only process the start script if there is more than one script...
if (_end < 2) return;
if (_scripts[0] == null) return;
/* invoke scripts[0] */
}
private void ProcessScriptBlockParameterSet() {
/* for (i in _start.._end) invoke _scripts[i] */
}
private void EndBlockParameterSet() {
/* invoke _endScript */
}
It's not entirely satisfactory, as it could be considered an implementation detail if it's not explicitly stated in the documentation, however it gives me enough confidence to assume that this is the intention. This pattern is also discussed in Powershell in Action which seems to have some sort of blessing from the powershell team (Snover at least).
I guess part of them going "open source" and being more "*nix-y" is defining the source code to be part of the documentation :)