PowerShell Input & Output
Up to this point in the Learn PowerShell series we have covered examples using very simple PowerShell input and output. Typically we have statically provided inputs by hard coding a variable (ex. $input = ‘SomeInput’). Our outputs so far have mostly consisted of Write-Host. In this lesson we’ll look to take in more dynamic input and show you how to take more control of your output.
Video
If you prefer video format over written documentation I discuss this topic in the following TechThoughts video:
PowerShell Input
Often times your PowerShell code will need to evaluate some type of input. That input can be statically provided by you ahead of time. There are often use cases though where retrieving a dynamic input is desired.
PowerShell Input From cmdlets
You can of course use the output of cmdlets for the input of your PowerShell functionality. Depending on what a cmdlet returns, you could perform a variety of different functions based on that generated input.
- run cmdlets (Get-Process)
- cmdlet generates output
- use output as your input
- perform functionality based on dynamic input
- use output as your input
- cmdlet generates output
Dynamic PowerShell input from the web
Lets explore an example where we use PowerShell to pull in dynamic results from a website. If you come from a more Linux focused background this is a bit like using curl.
#retrieve dynamic content from a website
$webResults = Invoke-WebRequest -Uri 'https://reddit.com/r/powershell.json'
$rawJSON = $webResults.Content
$objData = $rawJSON | ConvertFrom-Json
$posts = $objData.data.children.data
$posts | Select-Object Title,Score | Sort-Object Score -Descending
Here we are using Invoke-WebRequest to retrieve web content from /r/PowerShell. (A great PowerShell resource by the way!) HTML isn’t particularly useful to us as we want to work with objects in PowerShell. So note that I have visited the JSON version of this page. As a result, the output content from this cmdlet is in JSON. Because JSON is structured data, we can take control and convert it to native PowerShell objects using ConvertFrom-JSON.
Note: There are many of these convert cmdlets. This enables you to take in data from a variety of sources and continue to work with PowerShell objects!
Once in native object format, it’s a simple matter to explore the available sub-properties. We can then drill down into them and find post information. Once we have post data, we can select and sort for the inputs we are after. In this example, post title and score (upvotes) are returned.
Run the example above on your machine to see the results. Try exploring other available sub-properties in $posts.
Leveraging cmdlet output as input
How would you go about using the output from that example as input? That’s entirely up to you! You could write PowerShell code to:
- Email yourself the top 3 PowerShell posts of the day
- Send yourself a text message with the top post of the day
- Display the top 10 posts of the day when you login
You’d be using PowerShell to make yourself smarter on PowerShell! We’ll be covering more how to create ps1 scripts later in the series so stay tuned!
You’d be using PowerShell to make yourself smarter on PowerShell!
Read-Host Input
Read-Host is another way to get dynamic input from yourself or your users. Leveraging the previous example again, we could only return the desired number of posts:
[int]$numPosts = Read-Host -Prompt "Enter the number of posts to read"
#retrieve dynamic content from a website
$webResults = Invoke-WebRequest -Uri 'https://reddit.com/r/powershell.json'
$rawJSON = $webResults.Content
$objData = $rawJSON | ConvertFrom-Json
$posts = $objData.data.children.data
$posts | Select-Object Title,url | Sort-Object Score -Descending | Select-Object -First $numPosts
The -First parameter of Select-Object allows us to control how many objects we return to the console. Because we are sorting by number of upvotes, we will return the number of top posts specified. Run this on your computer specifying different numbers of posts.
Here is fun example where we can retrieve web data combined with Read-Host to display the desired number of cat facts:
[int]$numFacts = Read-Host -Prompt "Enter the number of cat facts you would like"
$webResults = Invoke-RestMethod -Uri "https://catfact.ninja/facts?limit=$numFacts&max_length=140"
$webResults.data
Note that catfact.ninja is an API and not a traditional webpage. As a result, Invoke-RestMethod is the appropriate cmdlet, and not Invoke-WebRequest.
Get-Content
Another source of dynamic input that you often need comes from files on a device. Operationally, you’ll often be looking at log files. Lets take a look at an example log file:
[INFO][Date]Normal Operations
[INFO][Date]Normal Operations
[INFO][Date]Normal Operations
[ERROR][Date]192.168.1.5 unable to access
[INFO][Date]Normal Operations
Get-Content can retrieve the contents of a file for you to use as input.
$logContent = Get-Content C:\Test\SampleLog.log
# get just entries that have an IP address using regex
$regex = "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"
$logContent | Select-String -Pattern $regex -AllMatches
# get just entries that have an IP address using Where-Object
$logContent | Where-Object {$_ -like "*.*.*.*"}
The contents of the SampleLog are stored in $logContent. Now that you have that input data you can start doing evaluations. It may be critical that PowerShell take some form of action if an IP address is discovered in the log. You can parse the data using regular expression (regex) or by using Where-Object. If an IP is discovered, you could ticket, pop a notification, or perform some other action.
You aren’t limited to just text or standard log files. Get-Content can pull in input data from a wide array of file types. Here is an example where you can get input from a csv file. Remember that there are many conversion cmdlets so you can get csv data into normal PowerShell object format easily!
$rawCSVInput = Get-Content C:\Test\SampleCSVFile.csv
$objData = $rawCSVInput | ConvertFrom-Csv
PowerShell Output
If you want to display results back to the console or generate a results file, you’ll need to generate some type of PowerShell output. There are a few ways to accomplish this, and one you should avoid!
Write-Host
Write-Host can output customized strings to the console. You can customize the output by controlling the start of a new line, or -NoNewline. A foreground and background color can also be set. Try the following examples:
# Output simple string to console
Write-Host 'Text output to console'
# Customize output to the console with colors:
Write-Host "Warning" -ForegroundColor Yellow
Write-Host "ERROR" -ForegroundColor Red
Write-Host "Works Great" -ForegroundColor Green
Write-Host "CRITICAL ERROR" -BackgroundColor Red -ForegroundColor White
Write-Host is only capable of string output, so you need to make sure you are giving it a string. If not, Write-Host will do its best to accommodate some type of output, but the results may not be what you expect.
$hostInfo = Get-Host
Write-Host $hostInfo
In the above example, you’ll get the output of: System.Management.Automation.Internal.Host.InternalHost
This is because $hostInfo is a PowerShell object, not a string. We can drill down farther into this object until we have a string that is use-able by Write-Host:
$hostInfo = Get-Host
Write-Host $hostInfo
Write-Host $hostInfo.Version
Write-Host is easy to implement, and it’s color capabilities make it an attractive choice for displaying results to users. However, you shouldn’t use it. It can be handy to use when writing new PowerShell to verify your code contains values you expect. Beyond that, its use should be avoided. Jeffrey Snover covers the reasons why in his post Write-Host Considered Harmful. In it he advocates for the use of Write-Verbose over Write-Host. We’ll be covering Write-Verbose later in the series when we begin creating PowerShell functions.
Using Write-Host is almost always wrong.
Jeffrey Snover – creator of PowerShell
Write-Output
If you’ve been following along in the series you’ve already used Write-Output quite a bit. This is because Write-Output is what PowerShell is using behind the scenes as the default output stream.
Get-Process
When you run Get-Process and see results in the PowerShell console, that was accomplished by Write-Output. All of these examples accomplish the same thing:
# Example 1
Get-Process
# Example 2
$processes = Get-Process
Write-Output $processes
# Example 3
$processes = Get-Process
$processes
As a result, you won’t often see Write-Output written in PowerShell code. It’s implied as the default output behavior.
Out-File
The console isn’t your only avenue for PowerShell output. For times when you want to output to a file, you can leverage Out-File. Out-File is capable of sending the entire output to file. This means that it’s a bit more intelligent than Write-Host, and can handle sending the entire object return from a cmdlet to file. See below for some examples:
# Example 1
$processes = Get-Process
$processes | Out-File -FilePath C:\Test\processInfo.txt
# Example 2
Get-Process | Out-File -FilePath C:\Test\processInfo.txt
Don’t forget about those conversion cmdlets! There are several for outputs as well! So, if your file needs to be in CSV format, you can leverage ConvertTo-CSV:
$processes = Get-Process
$processes | ConvertTo-Csv -NoTypeInformation | Out-File c:\test\processes.csv
There are a few parameters you’ll want to familiarize yourself with for Out-File.
- NoClobber – prevents an existing file from being overwritten
- Append – instead of overwriting an output file, add to it
- Encoding – especially useful if you are running on Linux – specify the encoding type of the file
- PS1 – Should you learn PowerShell?
- Learn and use PowerShell with just three commands
- Working with the PowerShell Pipeline
- PowerShell History and Current State
- Getting setup for PowerShell Development
- Working With PowerShell Variables
- Taking Control with PowerShell Logic
- PowerShell Input & Output
- PowerShell Errors and Exceptions Handling
- PowerShell Remoting
- PowerShell Scripts
- PowerShell Functions
- Manage Cloud with PowerShell
- PowerShell Modules