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.
CodePudding user response:
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 makeSet-Content
matchOut-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 asOut-File
, but it wouldn't be if the input contained complex objects, such as the[pscustomobject]
instances created by yourSelect
(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; the linked documentation doesn't discuss the behavior:
Set-Content
(and the closely relatedAdd-Content
)
By contrast, Out-File
and its effective alias >
create output files unconditionally - whether they receive input or not.
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:
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 asOut-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.- You may alternatively use
$null = Get-ChildItem ...
instead of appending| Out-Null
- You may alternatively use
Alternatively, 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 asOut-File.
-Streammakes it emit the formatted strings _one by one_, allowing
Set-Content` to write the results in a streaming fashion.- Alternatively, if you don't mind building the file content as a multi-line string in memory up front, omit
-Stream
and add-NoNewLine
to theSet-Content
call.
Finally, you may simply delete 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 }
CodePudding user response:
You should place all the services' names in a file or something. You can read the file using get-content
Then you can iterate each of the services names by using a foreach
loop.
For each of the services, you can check the service's status and based on that you can decide to write that in file or not.
Now note by, I have just given a simplified structure of what you were trying to achieve but since I am not sure whether you want the content to be appended in the same file or the file has to be overwritten or something similar; I just gave a simple statement so that you get the logic.
Further I'd like you to put try/catch
block and capture the error because most of the services wont be readily available. So, it is always better to capture the error message or at least the exception message in the catch block.
Also note, in case of nothing returned, then you can just put a file creation statement in the else block which will help you. It means that service is not stopped hence the file is being created as empty or may be you should just add the service name as the content of the file to know specifically which service corresponds to it.
You should do something like this:
try
{
$services = "AppHostSVC","FTPSVC", "IISAdmin"
foreach($service in $services)
{
if((Get-Service $Service).Status -eq 'Stopped')
{
Get-Service $Service| Select-Object Name,Status,DisplayName| ConvertTo-Html |Out-File -FilePath "C:\checker.html" -Append -Force
}
else
{
"$($Service) is not stopped"
}
}
}
catch
{
$_.Exception.Message
}
Hope it helps.