How to suppress PowerShell Get-Content output - powershell

I have a PS script:
script.ps1
[System.Xml.XmlDocument] $Config;
function Get-ScriptDirectory
{
Split-Path $script:MyInvocation.MyCommand.Path
}
function Load-Config
{
$configPath = Join-Path (Get-ScriptDirectory) config.xml
$global:Config = [xml](gc $configPath)
}
Load-Config
config.xml
<Configuration>
</Configuration>
Later in the script I'm working with $Config variable. When I run this script it writes the output to the console, which contains the root element of the xml. Something like:
Configuration
--------------
Is there exists any way how to suppress this output?
Thanks.

If you don't want the output of a command to be printed out to the console, you can discard it by piping it or redirecting it to Out-Null. For example both will work:
$Config | Out-Null
$Config > Out-Null
If you're familiar with Unix-like operating systems, Out-Null is conceptually equivalent to /dev/null.

Probably the output isn't caused by the assignment statement (a voidable statement), but by this line:
[System.Xml.XmlDocument] $Config;
In PowerShell, typically, all statements return a value (except for voidable statements). I think that the first time you run the script no output will be written to the console. However on subsequent runs $Config will still contain the value of the previous run, and its value will be written to the screen.
piping to the Out-Null cmdlet:
[System.Xml.XmlDocument] $Config | Out-Null
casting to void:
[void][System.Xml.XmlDocument]$Config
assigning to $null:
$null = $Config
or simply not 'declaring' the $Config variable
are ways to suppress this behaviour.

Somewhere you're dumping the variable from your script. As it falls out of the pipe it gets passed to Out-Host and that will yield the output you see.
The actual solution is to make sure you don't return anything from your script. As I cannot see your code I can't point to where, but somewhere there is a pipeline or statement that leaks the object into output. Are you sure you're using an assignment at every place you need?

A few Options:
# Pipe to the Out-Null cmdlet
$Config | Out-Null
# Cast to void
[void]$Config
# assign to $null
$null = $Config
# redirect to $null
$Config > $null

Related

Using Test-Path to find value item in hashtable

I have recently started using Set-StrictMode to get into better scripting habbits (i.e. declaring variables and such) and I have run into a small issue. For most of my scripts I will create a hashtable $Script = #{} and then declare all variables used within the script as sub properties under that because no matter what, when you start the script, all the variables will be clean and if you print out all the variables at the end of your script you know they will be from that specific session.
Historically if I needed to see if a subvariable such as $Script.RunOnce was declared I would just use If ($Script.RunOnce) {} but with strict mode you have to do something along the lines of this If (Test-Path Variable:\Script.WriteOnce) {} except test-path sees "Script.WriteOnce" as its own variable, not a sub variables underneath $Script
Why do I need to do this you may ask? Well I am writing a function that uses .Net Streamwriter and I want to make sure that if the variable "$WriteTee.StreamWriter" exists run the $WriteTee.StreamWriter.Close and $WriteTee.StreamWriter.Flush prior to declaring $Write-Tee again or else when I attempt to open a new streamwriter it will error out and I have to manually close the .net handle on the file before I can continue testing the script.
Long story short
Is there a way to test for $WriteTee.StreamWriter using Test-Path or some other way that doesn't create an error with Set-Strictmode
Super gutted example verison of my script.
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Inquire'
Function Write-Tee {
Begin {
#Variables that are needed only the first time the log function is started.
If ($WriteTee.RunOnce) {
If ($WriteTee.StreamWriter) { Write-Tee -Severity Error -Message "Log Writer Already Open. Attempting to close." -Close}
New-Variable -ErrorAction SilentlyContinue -Force -Name WriteTee -Value #{} -Scope Script
$Script:WriteTee.RunOnce = $True
}
}#End-Begin
Process {}#End-Process
End {
If ($Close -AND $Script:WriteTee) {
Write-Tee -Severity Info -Message "Flushing Log Writer and closing."
$WriteTee.StreamWriter.Flush()
$WriteTee.StreamWriter.Close()
Remove-Variable -ErrorAction SilentlyContinue -Force -Name WriteTee -Scope Script
Remove-Variable -ErrorAction SilentlyContinue -Force -Name WriteTee
}#End-If
}#End-End
}
Write-Tee -Message "Test" -Severity "Warning"
You can use test-path with the variable provider and the name of the variable to find out if a variable has been assigned to, but finding properties of a variable (or items in a hashtable, it's not clear which you are dealing with) requires other tactics:
To find out if a hashtable has an item with a key, you can do something like:
$writeTee.ContainsKey('StreamWriter') #returns $true or $false
To find out if a variable has a particular property, you can use get-member:
$writeTee | Get-Member -name StreamWriter #returns the member or nothing

How can I suppress/hide WMI output when using Setting an object? [duplicate]

I have a PS script:
script.ps1
[System.Xml.XmlDocument] $Config;
function Get-ScriptDirectory
{
Split-Path $script:MyInvocation.MyCommand.Path
}
function Load-Config
{
$configPath = Join-Path (Get-ScriptDirectory) config.xml
$global:Config = [xml](gc $configPath)
}
Load-Config
config.xml
<Configuration>
</Configuration>
Later in the script I'm working with $Config variable. When I run this script it writes the output to the console, which contains the root element of the xml. Something like:
Configuration
--------------
Is there exists any way how to suppress this output?
Thanks.
If you don't want the output of a command to be printed out to the console, you can discard it by piping it or redirecting it to Out-Null. For example both will work:
$Config | Out-Null
$Config > Out-Null
If you're familiar with Unix-like operating systems, Out-Null is conceptually equivalent to /dev/null.
Probably the output isn't caused by the assignment statement (a voidable statement), but by this line:
[System.Xml.XmlDocument] $Config;
In PowerShell, typically, all statements return a value (except for voidable statements). I think that the first time you run the script no output will be written to the console. However on subsequent runs $Config will still contain the value of the previous run, and its value will be written to the screen.
piping to the Out-Null cmdlet:
[System.Xml.XmlDocument] $Config | Out-Null
casting to void:
[void][System.Xml.XmlDocument]$Config
assigning to $null:
$null = $Config
or simply not 'declaring' the $Config variable
are ways to suppress this behaviour.
Somewhere you're dumping the variable from your script. As it falls out of the pipe it gets passed to Out-Host and that will yield the output you see.
The actual solution is to make sure you don't return anything from your script. As I cannot see your code I can't point to where, but somewhere there is a pipeline or statement that leaks the object into output. Are you sure you're using an assignment at every place you need?
A few Options:
# Pipe to the Out-Null cmdlet
$Config | Out-Null
# Cast to void
[void]$Config
# assign to $null
$null = $Config
# redirect to $null
$Config > $null

Powershell does not put system variable in path

Putting the following in a Powershell console, it outputs the variable value:
[Environment]::UserName
Putting this:
PS C:\> Test-Path C:\Users\${[Environment]::UserName}\AppData\Local\Microsoft\Outlook
False
Odd, so I went ahead and outputted to the console itself:
PS C:\> Write-Host C:\Users\${[Environment]::UserName}\AppData\Local\Microsoft\Outlook
C:\Users\\AppData\Local\Microsoft\Outlook
It returns blank. Why? Putting the variable directly to console does output its value while passing it like that it doesnt...
You should use $env:LOCALAPPDATA since you don't have to hard code c:\. You should also use the Join-Path cmdlet if you want to join a path:
$path = Join-Path $env:LOCALAPPDATA 'Microsoft\Outlook'
Are you sure the path being output is correct; i.e. that the username's appended? When I run it I see c:\Users\\AppData\...; i.e. no username.
Working code:
$fn = ("C:\Users\{0}\AppData\Local\Microsoft\Outlook" -f $env:Username)
Write-Host $fn
Test-Path $fn

Powershell pipe into exe and wait

I am piping an array of data into a executable program but I need it to block after every call in the foreach loop. It will leave the loop before it even opens the program from the first call.
Set-Alias program "whatever.exe"
foreach ($data in $all_data)
{
$data| %{ program /command:update /path:"$_" /closeonend:2 }
}
I like PowerShell but I never really learned Invoke-Command. So whenever I need to run an EXE I always use cmd. If you type cmd /? you get its help, look at the "c" switch. I'd do something like this:
foreach ($data in $all_data){
$data |
Foreach-Object{
cmd /c "whatever.exe" /command:update /path:"$_" /closeonend:2
}
}
If you don't like the cmd /c thing you could use Jobs.
foreach ($data in $all_data){
$data |
Foreach-Object{
$job = Start-Job -InitializationScript {Set-Alias program "whatever.exe"} -ScriptBlock {program /command:update /path:"$($args[0])" /closeonend:2} -ArgumentList $_
while($job.Status -eq 'Running'){
Start-Sleep -Seconds 3
#Could make it more robust and add some error checking.
}
}
}
I can think of two ways to tackle this:
pipe your executable call to Out-Null
shell out the call to cmd.exe /c (as shown in #BobLobLaw's answer)
I made your sample code a little more specific so I could run and test my solutions; hopefully it'll translate. Here's what I started with to be equivalent to your sample code, i.e. the script executes with no waiting on the executable to finish.
# I picked a specific program
Set-Alias program "notepad.exe"
# And put some values in $all_data, specifically the paths to three text files.
$all_data = Get-Item B:\matt\Documents\*.txt
# This opens each file in notepad; three instances of notepad are running
# when the script finishes executing.
$all_data | %{ program "$_" }
Here's the same code as above, but piping to Out-Null forces the script to wait on each iteration of the loop.
# I picked a specific program
Set-Alias program "notepad.exe"
# And put some values in $all_data, specifically the paths to three text files.
$all_data = Get-Item B:\matt\Documents\*.txt
# Piping the executable call to out-null forces the script execution to wait
# for the program to complete. So in this example, the first document opens
# in notepad, but the second won't open until the first one is closed, and so on.
$all_data | %{ program "$_" | Out-Null}
And, lastly, the same code (more or less) using cmd /c to call the executable and make the script wait.
# Still using notepad, but I couldn't work out the correct call for
# cmd.exe using Set-Alias. We can do something similar by putting
# the program name in a plain old variable, though.
#Set-Alias program "notepad.exe"
$program = "notepad.exe"
# Put some values in $all_data, specifically the paths to three text files.
$all_data = Get-Item B:\matt\Documents\*.txt
# This forces script execution to wait until the call to $program
# completes. Again, the first document opens in notepad, but the second
# won't open until the first one is closed, and so on.
$all_data | %{ cmd /c $program "$_" }
Depending on your scenario, wait-job might be overkill. If you have a programmatic way to know that whatever.exe has done its thing, you could try something like
do {start-sleep -sec 2} until ($done -eq $true)
Oh and.

'Get-ChildItem -Include/-Exclude' in PowerShell doesn't filter if passed as an argument to a function

I have a problem getting a filter argument to Get-ChildItem in a function.
The following works fine and displays a whole list of files:
c:\temp\Get-ChildItem -Include *deleteme*.txt -Recurse
Now say I have the following script
#file starts here
#filename = GetLastFile.ps1
param([string] $filter)
$files = Get-ChildItem $filter
Write-Host $files #should print all matching files but prints nothing
$file = $files | Select-Object -Last 1;
$file.name #returns filename
#File ends here
Now trying to run the script,
c:\temp.\GetLastFile.ps1 "-Include *deleteme*.txt -Recurse"
returns nothing.
Supplying a filter, *.*, works fine. It seems to be failing due to the -Include or -Exclude. Any ideas?
You're starting to get into an area where where Powershell 2.0 proxy functions can help. However, short of that, here's a simple way in PowerShell 2.0 to do this assuming all you need is -Include and -Recurse. Actually, I would recommend using -Filter instead it will do what you want and frankly it's quite a bit faster (4x on some of my tests) because -filter uses filesystem filtering provided by the OS whereas -include is processed by PowerShell.
param([string]$Filter, [switch]$Recurse)
$files = Get-ChildItem #PSBoundParameters
Write-Host $files #should print all matching files but prints nothing
$file = $files | Select-Object -Last 1;
$file.name #returns filename
The # symbol is used to "splat" an array or hashtable across the parameters to a command. The $PSBoundParameters variable is an automatic variable new to PowerShell 2.0 that is defined in functions. It's a hashtable that contains all the bounded (named and positional) parameters e.g.:
PS> function foo($Name,$LName,[switch]$Recurse) { $PSBoundParameters }
PS> foo -Name Keith Hill -Recurse
Key Value
--- -----
Name Keith
Recurse True
LName Hill
When you splat a hashtable like this against a command, PowerShell will map the key's (e.g. Recurse) value to the parameter named Recurse on the command.
I believe what is happening is your $filter parameter is being treated as a single string argument to the Get-ChildItem command. As such, unless you have a directory named "-Include deleteme.txt -Recurse", the command will always return nothing.
As for fixing your problem, well, there are a bunch of ways you could approach it. Probably one of the more versatile ways is to switch program behavior if the the $filter argument is passed, and instead of passing the entire filter string just pass the "deleteme.txt" string.
You can use Invoke-Expression to execute a command stored in a variable. For example:
param([string] $filter)
$files = Invoke-Expression "Get-ChildItem $filter"
Write-Host $files
$file = $files | Select-Object -Last 1
$file.name