Get-Service AppHostSVC,FTPSVC,IISAdmin,MSFTPSVC,W3SVC,WAS,WMSVC |
Select Name,Status,DisplayName|
Where-Object {$_.Status -EQ "Stopped"}
ConvertTo-Html |
Out-File -FilePath c:\checker.html
In the above cmdlet, how can I add a conditional file creation? I need the file to be created only when the said service are in STOPPED status.
To address the question as asked:
tl;dr
Courtesy of Santiago Squarzon's comments: Use Set-Content instead of Out-File, because Set-Content only creates its output file if it receives actual input:
# Create c:\checker.html only if any services are in the stopped state.
Get-Service AppHostSVC,FTPSVC,IISAdmin,MSFTPSVC,W3SVC,WAS,WMSVC |
Select Name,Status,DisplayName|
Where-Object {$_.Status -EQ "Stopped"}
ConvertTo-Html |
Set-Content -Encoding Unicode -FilePath c:\checker.html
Note:
-Encoding Unicode is meant for Windows PowerShell, to make Set-Content match Out-File's default character encoding there.
In PowerShell (Core) 7+, this isn't necessary, because all cmdlets use the same encoding there, namely BOM-less UTF-8.
Since ConvertTo-Html outputs strings, Set-Content in effect produces the same file content as Out-File, but it wouldn't be if the input contained complex objects, such as the [pscustomobject] instances created by your Select (Select-Object) command. Read on for background information and for a solution.
The following are the general-purpose file-writing cmdlets that create output files conditionally, namely only if they receive actual input; it is unclear whether that is by deliberate design; given that the linked documentation doesn't discuss the behavior, it is somewhat risky to rely on this behavior:
Set-Content (and the closely related Add-Content)
Tee-Object
By contrast, Out-File and its effective alias > create output files unconditionally - whether they receive input or not. Ultimately, this makes for more predictable behavior and is in line with how > works in POSIX-compatible shells (such as Bash).
Other - special-purpose - file-writing cmdlets exhibit one or the other behavior; e.g., Export-Csv and Export-CliXml unconditionally create, where as Invoke-RestMethod -OutFile does not.
Note the pitfall associated with conditional output-file creation:
If no file is created by a particular call, any preexisting output file is retained, which could be mistaken for the results of the call.
To avoid this, delete any preexisting file first.
Set-Content alone is not a full substitute for Out-File, because the latter applies the rich for-display formatting to complex objects that you normally see in the console, whereas Set-Content performs simple .ToString() stringification.
Note that the rich formatting Out-File produces is meant for the human observer, not for programmatic processing; for the latter, use a structured text format, such as CSV.
A general solution therefore requires additional work:
Before offering solutions that rely on Set-Content / Tee-Object's behavior, consider simply deleting an unconditionally created output file after the fact if it is found to be empty (which also avoids the pitfall discussed above):
# Unconditionally create the output file.
$outFile = 'out.txt'
Get-ChildItem -File *.txt |
Select-Object Name, Length |
Out-File $outFile
# Remove the output file if it is empty (size of 0 bytes).
if ((Get-Item $outFile).Length -eq 0) { Remove-Item $outFile }
If you deliberately want to keep any preexisting file and if it is acceptable to collect all output in memory first:
# Collect all output, if any, first.
$results =
Get-ChildItem -File *.txt |
Select-Object Name, Length
# Write the file only if there are results.
if ($results) { $results | Out-File out.txt }
If you do want to rely on the current conditional file-creation behavior of Set-Content and Tee-Object:
The simplest solution is to repurpose Tee-Object; using a simplified command:
# Only write to out.txt if at least one file matches.
Get-ChildItem -File *.txt |
Select-Object Name, Length |
Tee-Object -FilePath out.txt | Out-Null
Tee-Object applies the same output formatting as Out-File, and also uses the same default character encoding - but, as discussed, it only creates the output file if actual input is received.
Because Tee-Object's purpose is to also pass input through, in addition to writing to a file, Out-Null is used to suppress any output from it.
$null = Get-ChildItem ... instead of appending | Out-NullAlternatively, combine Out-String with Set-Content:
# Only write to out.txt if at least one file matches.
Get-ChildItem -File *.txt |
Select-Object Name, Length |
Out-String -Stream |
Set-Content -Encoding Unicode out.txt
Note:
Out-String performs the same output formatting as Out-File. -Streammakes it emit the formatted strings _one by one_, allowingSet-Content` to write the results in a streaming fashion.-Stream and add -NoNewLine to the Set-Content call.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With