Powershell outputs vertically - powershell

I just wanted to run a simple command like echo #profile, but the output is vertical. I can theoretically read and understand the output, but it is a big unconvenience. How can I fix it?

You don't normally reference variables with an # symbol, you almost always use the $ to reference a variable's value by the variable name. You can also use the Variable: provider or Get-Variable, but I won't get into those here.
If you were to omit echo, you would actually get the following error message:
The splatting operator '#' cannot be used to reference variables in an expression.
'#var' can be used only as an argument to a command. To reference variables
in an expression use '$var'.
This is because using #var is a technique called Splatting, which is the practice of using an array or hashtable to provide the arguments to a command, function, or cmdlet. Note you cannot currently splat arguments to methods. Review my answer linked above for more information on how to actually splat arguments in several use cases.
As for why you get the vertical output, note that echo is actually an alias to Write-Output. Write-Output accepts positional arguments, for which it will output each object passed in on its own line. When you splat a string as an array of arguments to a function, it converts the string to an array of characters, so effectively you are passing in each character of #profile to Write-Output as its own argument, then spitting each element of the array back out. And when PowerShell displays an array directly to the console, it displays each element on its own line.
Note: Of the different Write- cmdlets, Write-Output is unique in that it will output each positional parameter on its own line as it returns an array for each argument you pass in. The other Write- cmdlets will instead join each element of the array into a single space-delimited string. The Write- cmdlets come into play when working with the different output streams in PowerShell. Here is another answer of mine which explains what the different output streams mean and how to write to them.
In addition, for displaying purely information text to the end user that does not need additional programmatic processing in your script or session, use Write-Host.
This is why you get the vertical output, because #profile is being converted to an array, then splatted into Write-Output as an array of characters, and Write-Output will write back all arguments of an array as individual elements of a new array. PowerShell will then display the new array with each element on its own line.
I suspect what you actually want to do is output $profile. You can use one of the following techniques (note that echo/Write-Output are often redundant to use):
# Use the alias
echo $profile
# Use Write-Output
Write-Output $profile
# Omit Write-Output entirely
$profile
# View one of the alternative profiles by name
# CurrentUserCurrentHost is the default
# and is most often the one you are looking for
$profile.CurrentUserCurrentHost
$profile.CurrentUserAllHosts
$profile.AllUsersCurrentHost
$profile.AllUsersAllHosts

Related

PowerShell 5.1 Can someone please explain hashtable and splatting

Given:
PowerShell 5.1
I'm having a little trouble understanding hashtable and splatting. When splatting are use using a hash table to do that or is it something completely different?
I have the following code:
$hashtable1 = #{}
$hashtable1.add('PROD',#{ FirstName = 'John'; LastName = 'Smith'})
function Main() {
$sel = $hashtable1['PROD']
Function1 $sel
Function2 #sel
}
function Function1([hashtable] $x) {
"Value: $($x.LastName)"
$x.FirstName
}
function Function2([string] $firstName) {
"Value: $($firstName)"
}
Main
There's good information in the existing answers, but let me attempt a focused summary:
The answer to your actual question is:
Yes, #{ FirstName = 'John'; LastName = 'Smith' } is a hashtable too, namely in the form of a declarative hashtable literal - just like #{} is an empty hashtable literal (it constructs an instance that initially has no entries).
A hashtable literal consists of zero or more key-value pairs, with = separating each key from its value, and pairs being separated with ; or newlines.
Keys usually do not require quoting (e.g. FirstName), except if they contain special characters such as spaces or if they're provided via an expression, such as a variable reference; see this answer for details.
This contrasts with adding entries to a hashtable later, programmatically, as your $hashtable1.Add('PROD', ...) method call exemplifies (where PROD is the entry key, and ... is a placeholder for the entry value).
Note that a more convenient alternative to using the .Add() method is to use an index expression or even dot notation (property-like access), though note that it situationally either adds an entry or updates an existing one: $hashtable1['PROD'] = ... or $hashtable1.PROD = ...
The answer to the broader question implied by your question's title:
PowerShell's hashtables are a kind of data structure often called a dictionary or, in other languages, associative array or map. Specifically, they are case-insensitive instances of the .NET [hashtable] (System.Collections.Hashtable) type, which is a collection of unordered key-value pair entries. Hashtables enable efficient lookup of values by their associated keys.
Via syntactic sugar [ordered] #{ ... }, i.e. by placing [ordered] before a hashtable literal, PowerShell offers a case-insensitive ordered dictionary that maintains the entry-definition order and allows access by positional index in addition to the usual key-based access. Such ordered hashtables are case-insensitive instances of the .NET System.Collections.Specialized.OrderedDictionary type.
A quick example:
# Create an ordered hashtable (omit [ordered] for an unordered one).
$dict = [ordered] #{ foo = 1; bar = 'two' }
# All of the following return 'two'
$dict['bar'] # key-based access
$dict.bar # ditto, with dot notation (property-like access)
$dict[1] # index-based access; the 2nd entry's value.
Splatting is an argument-passing technique that enables passing arguments indirectly, via a variable containing a data structure encoding the arguments, which is useful for dynamically constructing arguments and making calls with many arguments more readable.
Typically and robustly - but only when calling PowerShell commands with declared parameters - that data structure is a hashtable, whose entry keys must match the names of the target command's parameters (e.g., key Path targets parameter -Path) and whose entry values specify the value to pass.
In other words: This form of splatting uses a hashtable to implement passing named arguments (parameter values preceded by the name of the target parameter, such as -Path /foo in direct argument passing).
A quick example:
# Define the hashtable of arguments (parameter name-value pairs)
# Note that File = $true is equivalent to the -File switch.
$argsHash = #{ LiteralPath = 'C:\Windows'; File = $true }
# Note the use of "#" instead of "$"; equivalent to:
# Get-ChildItem -LiteralPath 'C:\Windows' -File
Get-ChildItem #argsHash
Alternatively, an array may be used for splatting, comprising parameter values only, which are then passed positionally to the target command.
In other words: This form of splatting uses an array to implement passing positional arguments (parameter values only).
This form is typically only useful:
when calling PowerShell scripts or functions that do not formally declare parameters and access their - invariably positional - arguments via the automatic $args variable
when calling external programs; note that from PowerShell's perspective there's no concept of named arguments when calling external programs, as PowerShell's knows nothing about the parameter syntax in that case, and all arguments are simply placed on the process command line one by one, and it is up to the target program to interpret them as parameter names vs. values.
A quick example:
# Define an array of arguments (parameter values)
$argsArray = 'foo', 'bar'
# Note the use of "#" instead of "$", though due to calling an
# *external program* here, you may use "$" as well; equivalent to:
# cmd /c echo 'foo' 'bar'
cmd /c echo #argsArray
#postanote has some very good links about hashtables and splatting and are good reads. Taking your examples, you have two different functions. One to handle hashtables as a parameter, and the second one that can only handle a single string parameter. The hashtable cannot be used to pass parameters to the second function, e.g.:
PS C:\> Function2 $sel
Value: System.Collections.Hashtable
Conceptually, the real difference between using hashtables and splatting is not about how you are using them to pass information and parameters to functions, but how the functions and their parameters receive the information.
Yes, certain functions can have hashtables and arrays as parameters, but, typically in 98% of the cases, functions don't use hashtables as a named parameter to get its values.
For ex. Copy-Item doesn't use hash tables as a parameter. If it did, would you want to do this every time you want to copy anything:
$hashtable = #{
Path = "C:\Temp\myfile.txt",
Destination = "C:\New Folder\"
}
Copy-Item -Parameters $hashtable
No, instead, you want the parameters as strings, so you can make it a much easier one liner:
Copy-Item -Path "C:\Temp\myfile.txt" -Destination "C:\New Folder\"
It makes more sense to most people to deal with individual strings as opposed to a generic, large, hashtable "config" to pass. Also, by separating the parameters as separate strings, integers, floats, chars, etc. it is also easier to do validation, default values, mandatory/not mandatory parameters, etc.
Now despite saying all that, there is a situation where you have certain functions with lots of parameters (e.g. sending an email message), or you want to do something multiple times (e.g. copying/moving lots of files from a CSV file). In that case, using a hashtable, and/or arrays, and/or arrays of hashtables, would be useful.
This is where splating comes in. It takes a hashtable and instead of treating it like you are passing a single value (i.e. why Function2 $sel returns System.Collections.Hashtable), the # signt tells PowerShell that it is a collection of values, and to use the collection to try and match to the parameters of the function. That's why passing the hashtable to Function2 doesn't work, but splatting works e.g.:
PS C:\> Function2 #sel
Value: John
In this case, it takes the hashtable $sel and by using splatting #sel PowerShell now knows to not pass the hashtable as-is, but to open up the collection and to matche the $sel.FirstName to the -Firstname parameter.

Powershell function returns stdout? (Old title: Powershell append to array of arrays appends stdout?)

I've learnt from this thread how to append to an array of arrays. However, I've observed the weirdest behaviour ever. It seems to append stdout too! Suppose I want to append to an array of arrays, but I want to echo debug messages in the loop. Consider the following function.
function WTF {
$Result = #()
$A = 1,2
$B = 11,22
$A,$B | % {
Write-Output "Appending..."
$Result += , $_
}
return $Result
}
Now if you do $Thing = WTF, you might think you get a $Thing that is an array of two arrays: $(1,2) and $(11,22). But that's not the case. If you type $Thing in the terminal, you actually get:
Appending...
Appending...
1
2
11
22
That is, $Thing[0] is the string "Appending...", $Thing[1] is the string "Appending...", $Thing[2] is the array #(1,2), and $Thing[3] is the array #(11,22). Nevermind that they seem to be in a weird order, which is a whole other can of worms I don't want to get into, but why does the echoed string "Appending..." get appended to the result??? This is so extremely weird. How do I stop it from doing that?
EDIT
Oh wait, upon further experimenting, the following simpler function might be more revealing:
function WTF {
$Result = #()
1,2 | % {
Write-Output "LOL"
}
return $Result
Now if you do $Thing = WTF, then $Thing becomes an array of length 2, containing the string "LOL" twice. Clearly there is something fundamental about Powershell loops and or Write-Output that I'm not understanding.
ANOTHER EDIT!!!
In fact, the following even simpler function does the same thing:
function WTF {
1,2 | % {
Write-Output "LOL"
}
}
Maybe I just shouldn't be using Write-Output, but should use Write-Information or Write-Debug instead.
PowerShell doesn't have return values, it has a success output stream (the analog of stdout in traditional shells).
The PowerShell pipeline serves as the conduit for this stream, both when capturing command output in a variable and when sending it to another command via |, the pipeline operator
Any statement - including multiple ones, possibly in a loop - inside a function or script can write to that stream, typically implicitly - by not capturing, suppressing, or redirecting output - or explicitly, with Write-Output, although its use is rarely needed - see this answer for more information.
Output is sent instantly to the success output stream, as it is being produced - even before the script or function exits.
return exists for flow control, independently of PowerShell's output behavior; as syntactic sugar you may also use it to write to the output stream; that is, return $Result is syntactic sugar for: $Result; return, with $Result by itself producing implicit output, and return exiting the scope.
To avoid polluting the success output stream - intended for data output only - with status messages, write to one of the other available output streams - see the conceptual about_Redirection help topic.
Write-Verbose is a good choice, because it is silent by default, and can be activated on demand, either via $VerbosePreference = 'Continue', or, on a per-call basis, with the common -Verbose parameter, assuming the script or function is an advanced one.
Write-Host, by contrast, unconditionally prints information to the host (display), and allows control over formatting, notably coloring.
Outputting a collection (array) from a script or function enumerates it. That is, instead of sending the collection itself, as a whole, its elements are sent to PowerShell's pipeline, one by one, a process called streaming.
PowerShell commands generally expect streaming output, and may not behave as expected if you output a collection as a whole.
When you do want to output a collection as a whole (which may sometimes be necessary for performance reasons), wrap them in a transient aux. single-element array, using the unary form of ,, the array constructor operator: , $Result
A conceptually clearer (but less efficient) alternative is to use Write-Output -NoEnumerate
See this answer for more information.
Therefore, the PowerShell idiomatic reformulation of your function is:
function WTF {
# Even though no parameters are declared,
# these two lines are needed to activate support for the -Verbose switch,
# which implicitly make the function an *advanced* one.
# Even without it, you could still control verbose output via
# the $VerbosePreference preference variable.
[CmdletBinding()]
param()
$A = 1,2
$B = 11,22
# Use Write-Verbose for status information.
# It is silent by default, but if you pass -Verbose
# on invocation or set $VerbosePreference = 'Continue', you'll
# see the message.
Write-Verbose 'Appending...'
# Construct an array containing the input arrays as elements
# and output it, which *enumerates* it, meaning that each
# input array is output by itself, as a whole.
# If you need to output the nested array *as a whole*, use
# , ($A, $B)
$A, $B
}
Sample invocation:
PS> $nestedArray = WTF -Verbose
VERBOSE: Appending...
Note:
Only the success output (stream 1) was captured in variable $nestedArray, whereas the verbose output (stream 4) was passsed through to the display.
$nestedArray ends up as an array - even though $A and $B were in effect streamed separately - because PowerShell automatically collects multiple streamed objects in an array, which is always of type [object[]].
A notable pitfall is that if there's only one output object, it is assigned as-is, not wrapped in an array.
To ensure that a command's output is is always an array, even in the case of single-object output:
You can enclose the command in #(...), the array-subexpression operator
$txtFiles = #(Get-ChildItem *.txt)
In the case of a variable assignment, you can also use a type constraint with [array] (effectively the same as [object[]]):
[array] $txtFiles = Get-ChildItem *.txt
However, note that if a given single output object itself is a collection (which, as discussed, is unusual), no extra array wrapper is created by the commands above, and if that collection is of a type other than an array, it will be converted to a regular [object[]] array.
Additionally, if #(...) is applied to a strongly typed array (e.g., [int[]] (1, 2), it is in effect enumerated and rebuilt as an [object[]] array; by contrast, the [array] type constraint (cast) preserves such an array as-is.
As for your specific observations and questions:
I've learnt from this thread how to append to an array of arrays
While using += in order to incrementally build an array is convenient, it is also inefficient, because a new array must be constructed every time - see this answer for how to construct arrays efficiently, and this answer for how to use an efficiently extensible list type as an alternative.
Nevermind that they seem to be in a weird order, which is a whole other can of worms I don't want to get into
Output to the pipeline - whether implicit or explicit with Write-Output is instantly sent to the success output stream.
Thus, your Write-Output output came first, given that you didn't output the $Result array until later, via return.
How do I stop it from doing that?
As discussed, don't use Write-Output for status messages.

How can I splat a hashtable directly from a class static method?

How can I get the same output as:
$ht = #{Object="Hi there";Foregroundcolor="Green"}
Write-Host #ht
without using a/the variable $ht ?
Don't get me wrong, I know how to use a basic CMDLet.
I have a static method that generates dynamic hashtables.
Look at this simplified example code:
class HashtableGenerator
{
static [hashtable]Answer()
{
return #{Object="Hallo Welt";ForegroundColor="Green"}
}
}
$ht = [HashtableGenerator]::Answer()
Write-Host #ht
This works just fine, but is it possible to get rid of the $ht variable, so the code would look something like this:
Write-Host #([HashtableGenerator]::Answer()) # Doesn't work
I'm pretty sure what you are looking to do is not possible at least at this time. Splatting is specific to hashtable and array variables explicitly. Not return values of functions, methods etc. Technet for splatting sort of supports this
Splatting is a method of passing a collection of parameter values to a command as unit. PowerShell associates each value in the collection with a command parameter. Splatted parameter values are stored in named splatting variables, which look like standard variables, but begin with an At symbol (#) instead of a dollar sign ($). The At symbol tells PowerShell that you are passing a collection of values, instead of a single value.
Using the # outside of that will tell PowerShell to treat the results as an array. IIRC there is a semi related feature request to splat directly from a hashtable definition instead of saving to a variable first.
Related question talking about splatting from a variable property: Splatting a function with an object's property
Passing function parameters using hashtable is just the same way you implicitly specify them. Hashtable key works as parameter name and its corresponding hashtable value uses as parameter value.
Write-Host -Object 'Hi there' -Foregroundcolor 'Green'

What is the Powershell equivalent for bash $*?

In other words how can I get the command line of the script itself?
So, I know about $PSBoundParameters, but it is not the same. I just want to get the string containing the passed in parameters as is.
How do I do it?
See get-help about_Automatic_Variables.
$Args
Contains an array of the undeclared parameters and/or parameter
values that are passed to a function, script, or script block.
When you create a function, you can declare the parameters by using the
param keyword or by adding a comma-separated list of parameters in
parentheses after the function name.
In an event action, the $Args variable contains objects that represent
the event arguments of the event that is being processed. This variable
is populated only within the Action block of an event registration
command. The value of this variable can also be found in the SourceArgs
property of the PSEventArgs object (System.Management.Automation.PSEventArgs)
that Get-Event returns.
Example:
test.ps1
param (
)
Write-Output "Args:"
$args
Output:
PS L:\test> .\test.ps1 foo bar, 'this is extra'
Args:
foo
bar
this is extra
$MyInvocation.Line
Read about_Automatic_Variables:
$MyInvocation
Contains an information about the current command, such as the name,
parameters, parameter values, and information about how the command was
started, called, or "invoked," such as the name of the script that called
the current command.
$MyInvocation is populated only for scripts, function, and script blocks.
just want to get the string containing the passed in parameters as is.
You're looking for a powershell equivalent of "$#", not $*.
And the other answers in the thread are not equivalent. The best quick way I have found for this is:
$args | join-string -sep ' '
which can in turn be used for any string array you may have on hand, not just the $args array.

Powershell script from another script with multiple positional arguments

There are already some questions about how to call one PS script with arguments from another like that one
Powershell: how to invoke a second script with arguments from a script
But I'm stuck in case I have first script with multiple positional parameters.
testpar2.ps1 calls testpars.ps1
#$arglist=(some call to database to return argument_string)
$arglist="first_argument second_argument third_argument"
$cmd=".\testpars.ps1"
& $cmd $arglist
$arglist variable should be populated with string from database. This string contains arguments for testpar.ps1.
testpars.ps1 looks like
echo argument1 is $args[0]
echo argument2 is $args[1]
echo arugment3 is $args[3]
# some_command_call $arg[0] $arg[1] $arg[2]
This arguments should be used in testpars.ps1 in some way, like to path them to some command.
But when i run testpars2.ps1 i got
argument1 is first_argument second_argument third argument
argument2 is
arugment3 is
It thinks that it is one argument, not a list of them.
As you can see, when you pass a string to a function, PowerShell treats that as a single value. This is usually good, because it avoids the problem in CMD of having to constantly quote and unquote strings. To get separate values, you need to split() the string into an array.
$arglistArray = $arglist.split()
Now you have an array of three strings, but they're still all passed as one parameter. PowerShell has an idea known as splatting to pass an array of values as multiple arguments. To use splatting, replace the $ with # in the argument list.
& $cmd #arglistArray
For more information on splatting, type Get-Help about_Splatting