Format command output in powershell - powershell

I have a output like below from a command
IpPrefix
--------
15.181.232.0/21
142.4.160.136/29
3.2.0.0/24
161.188.154.0/23
Using above output, need write a command in powershell to get format like :
15.181.232.0/21,142.4.160.136/29,3.2.0.0/24,161.188.154.0/23
Basically combine multiple lines to one line with comma separated. Please help.

Welcome to PowerShell! The results of commands are objects. This object you have made has at least 1 property called IpPrefix. If your object is stored in a variable $object, then you can reference the property like this:
$object = (some-command -param something)
$object.IpPrefix
Once you're got the items in just that one column, without the column header, you now have an array of strings. They're still objects, but they are string objects.
The -join operator works against arrays.
$object = (some-command -param something)
$object.IpPrefix -join ','
This will give you what you want in the simplest way possible.
Let's say maybe you don't want to store your data in a variable (aka in Memory). You might have a pipeline or some other situation where storing the data slows you down. You would do that like this:
(some-command -param something).IpPrefix -join ','
Same idea, different syntax. Hope this helps you understand the shell better!

Related

How to remove strings portion in variable and write those values back to variable in one-liner

i have problem like this, let's say i have an array like this:
$PSnames ='Win10-PS' , 'Server-PS', 'Client-PS'
Now I want to remove the “-PS” portion of each name in the $PSnames variable AND write those values back to the variable but this must be a one-liner. I tried using the foreach-object cmdlet with the pipeline, but the S in Server-PS is also lost.
$PSnames
Win10-PS
Client-PS
Server-PS
$PSnames | ForEach-Object {$_.Trim("-PS")}
Win10
Client
erver
I don't know how to handle and re-insert -PS portion in one-liner.
Could anyone give me an explanation as well as the solution? Thanks and i'm appreciate it!
The .Trim() method does not work as you probably think it does. ;-) I'd recommend using the -replace operator like this:
$PSnames ='Win10-PS' , 'Server-PS', 'Client-PS'
$PSnames = $PSnames -replace '-PS'

Powershell outputs vertically

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

Editing Powershell Object

I'm using powershell to run a command like so:
$getlist=rclone sha1sum remote:"\My Pictures\2009\03" --dry-run
Write-Output $getlist
that outputs a object with the results. Problem being I only want the first column of those results. I've tried things like custom-format --Depth 1 and the other *-format commands but they don't work on this object??
that outputs a object with the results
While that is technically true, it is more specifically an [object[]]-typed array of lines ([string] instances) that assigning the stream of output lines - produced by the external rclone program - to a PowerShell variable implicitly created. (Arrays created by PowerShell are [object[]]-typed, even if all the elements are of the same type, such as [string] in this case).
PowerShell fundamentally only "speaks text" when communicating with external programs.
Therefore, to extract substrings from these lines you must perform text parsing, as implied by AdminOfThings' comment on the question.
A simplified approach is to use the unary form of the -split operator:
# Simulate lines input whose first whitespace-separated token is to
# be extracted.
$getlist = 'foo bar baz', 'more stuff here'
$getlist.ForEach({ (-split $_)[0] })
The above yields:
foo
more
zett42's helpful answer shows a simpler alternative that relies on the -replace operator's (among others) ability to operate directly on each element of an array-valued LHS.
However, the -split approach is useful if you want to extract multiple column values.
If you don't need / want to capture all of the external program's (rclone's) output in memory first, you can use streaming processing in the pipeline, via the ForEach-Object cmdlet:
'foo bar baz', 'more stuff here' | ForEach-Object { (-split $_)[0] }
Note: While slightly slower than collecting all lines in memory up front, the advantage of a pipeline-based approach is reduced memory load: only the extracted substrings are kept in memory (if assigned to a variable).
You can use a regular expression to remove the undesired parts of the output:
$getlist = $getlist -replace '\s.*'
When a PowerShell operator such as -replace is applied to a collection, it will be applied to each element individually, creating a new array that stores the results (see Substitution in a collection).
The regular expression removes everything from the first whitespace up to the end of the string.
RegEx breakdown:
\s - a single whitespace character like space and tab
.* - any character, zero or more times

Is there an easy way to have Powershell always show the actual value?

These things drive me nuts:
Is there an easy way to have Powershell just show me the empty string and the list with an empty string in it?
For a while I am maintaining a ConvertTo-Expression which converts an (complex) object into a PowerShell expression which eventually can be used to rebuild most objects. It might useful in situations as comparing test results but also to reveal objects. For details see: readme.
Source and installation
The ConvertTo-Expression script can be installed from the PowerShell Gallery:
Install-Script -Name ConvertTo-Expression
As it concerns a standalone script, installation isn't really required. If you don't have administrator rights, you might just download the script (or copy it) to the required location. You might than simply invoke the script using PowerShell dot sourcing:
. .\ConvertTo-Expression.ps1
Example
The following command outputs the same expression as used to build the object:
$Object = [Ordered]#{
'Null' = $Null
'EmptyString' = ''
'EmptyArray' = #()
'SingleItem' = ,''
'EmptyHashtable' = #{}
}
ConvertTo-Expression $Object
Note the comment from #Mathias there's no functional difference between "one string" and "an array of one string", the pipeline consumes 1 string either way. PowerShell is not node which is described here: PowerShell enumerate an array that contains only one inner array. Some objects might be really different than you expect.
See also: Save hash table in PowerShell object notation (PSON)
This is PowerShell, not Node. So it's not JavaScript or JSON. Also, PowerShell is not Bash or CMD any other regular text-based shell. PowerShell works with objects. .NET objects, in particular. And how objects are represented as text is ... quite a matter of taste. How to represent null? Of course: nothing. How to represent an empty string? Nothing, either. An empty array ... you get my point.
All pipeline output is by default send to Out-Default. In general, the way objects are represented can be controlled by format files: about_Format.ps1xml and about_Types.ps1xml. From PowerShell 6 upwards, the default formats are compiled into the source code, but you can extend them. How you do so, depends on your personal taste. Some options were already mentioned ConvertTo-Json "", ConvertTo-Json #("")), but this would be veryyy JSON-specific.
tl;dr Don't care too much about how objects are represented textually. As you see, there are many possible ways to do so, and also some others. Just make sure your scripts are always object-oriented.
You mean like Python's repr() function? A serialization? "Give me a canonical representation of the object that, when property evaluated, will return an object of the type originally passed?" No, that's not possible unless you write it yourself or you serialize it to XML, JSON, or similar. That's what the *-CliXml and ConvertTo-*/ConvertFrom-* commands are for.
On Powershell 7:
PS C:\> ''.Split(',') | ConvertTo-Json -Compress -AsArray
[""]
PS C:\> '1,2,3,,5'.Split(',') | ConvertTo-Json -Compress -AsArray
["1","2","3","","5"]
The closest would be the ToString() common method. However, that's intended for formatting output and typecasting, not canonical representations or serializations. Not every object even has a string representation that can be converted back into that object. The language isn't designed with that in mind. It's based on C#, a compiled language that favors binary serializations. Javascript requires essentially everything to have a string serialization in order to fulfill it's original design. Python, too, has string representations as fundamental to it's design which is why the repr() function is foundational for serialization and introspection and so on.
C# and .Net go the other way. In a .Net application, if you want to specify a literal empty string you're encouraged to use String.Empty ([String]::Empty in Powershell) because it's easier to see that it's explicit. Why would you ever want a compiled application to tell the user how the canonical representation of an object in memory? You see why that's not a useful thing for C# even if it might be for Powershell?

Powershell appcmd.exe trying to execute

I hava to add some environment to appPools? and i tried this code:
$Appcmd = [System.Environment]::SystemDirectory + "\inetsrv\appcmd.exe"
& $appcmd --% set config -section:system.applicationHost/applicationPools /+""[name='$Task.eProto_Pool'].environmentVariables.[name='PRODUCT_NAME',value='eProto']"" /commit:apphost"
but $Task in second line does not work, How can I past a variable to this string? I also tried %Task%
.eProto_Pool is a property of $Task. If you want to dereference (that is, retrieve one single property of an object) within a string, you need to wrap the string with $(), the subexpression operator in PowerShell.
For example, I'll make a new hashtable called $MyString that has two properties.
$MyString = #{Name = "Stephen";Value="CoolDude"}
>$MyString
Name Value
---- -----
Value CoolDude
Name Stephen
Look what happens if I try to reference it inside a string with regular string expansion. This is basically what you were doing in your example above. See how it fails to work as you would expect?
write-host " The user $MyString.Name is a $MyString.Value"
The user System.Collections.Hashtable.Name is a System.Collections.Hashtable.Value
Time to use the subexpression operator to save the day.
write-host " The user $($MyString.Name) is a $($MyString.Value)"
The user Stephen is a CoolDude
When in doubt, subexpression it out.
On second glance
I think it might be the percentage sign % which is causing you grief. This is a shorthand for the ForEach-Object command in PowerShell. Try this instead:
Invoke-expression "$appcmd --% set config -section:system.applicationHost/applicationPools /+`"`"[name='$($Task.eProto_Pool)'].environmentVariables.[name='PRODUCT_NAME',value='eProto']`"`" /commit:apphost`""
This should escape the strings like you need, and also pass the parameters in, like the eProto_Pool property of $Task.