I'm really new to powershell (I've used javascript a bit before), and I was wondering if there's a way to have a powershell script continue running though a loop until a user inputs a specific command to override the loop and break out of it? I've been able to pause the program to wait for a response, but I need the loop to continue running until someone enters a string such as "end." For example, in the following bit of code I wanted to break the while loop and complete a specific command if the user typed "quit," "charger on," or "charger off" at any point.
$overrideProgram = "null"
$overrideProgram = Read-Host
while ($overrideProgram -ne "quit",$overrideProgram -ne "charger on",$overrideProgram -ne "charger off") {
while ($true) {
$chargeLevel = (Get-WmiObject win32_battery).estimatedChargeRemaining #defining the variable "chargeLevel" as the charge percentage
if ($chargeLevel -le 40) {chargerOn}
if ($chargeLevel -ge 80) {chargerOff}
Start-Sleep -s 30 # 30 second delay before next check
}
}
Thanks so much.
Best way I can think to do this is to start a background job and use a script to control the termination of that job:
$job = start-job -scriptblock {
while ($true) {
$chargeLevel = (Get-WmiObject win32_battery).estimatedChargeRemaining #defining the variable "chargeLevel" as the charge percentage
if ($chargeLevel -le 40) {chargerOff}
if ($chargeLevel -ge 80) {chargerOn}
Start-Sleep -s 30 # 30 second delay before next check
}
}
Write-Host "Background job started with id: $($job.id)"
$run = $true
while ($run) {
$op = Read-Host -Prompt "What ya wanna do?"
switch($op) {
"quit" { $run = $false }
"charger on" { $run = $false }
"charger off" { $run = $false }
default { continue }
}
}
stop-job $job.id
receive-job $job.id # If you want to see the output
remove-job $job.id
In the above, a job is started and then your script will just sit in a loop waiting for the correct input. Once it has that, it will exit the loop and proceed to stop and remove the job that was started to begin with.
You will need to extend the contents of the -scriptblock argument of Start-Job to include the chargerOn and chargerOff definitions. If the script is going to be considerably complex, probably best to save it to a file and use the -FilePath argument of Start-Job
Related
I have a software that runs always when I'm out of home, but lately I noticed it could totally burn off my CPU if something goes wrong, so I need a code to put in a bat file that will kill the process if the CPU will reach the X temperature (in example, over 70°).
Done it in PowerShell Script.
For those who may need it in the future, it goes in loop and check high CPU usage.
function CheckCPU {
$CPU = (gwmi Win32_PerfFormattedData_PerfProc_Process | ? {$_.Name -like
'paint'}).PercentProcessorTime
}
function CheckProcess {
if($CPU -ge 70)
{
Get-Process -Name paint| Stop-Process
}
Else
{
echo "-------------------------------------------------------------------"
Get-Date -Format g
echo "All Ok: Usage of paint is $CPU % of CPU - Re-check in next 10 seconds."
echo "-------------------------------------------------------------------"
Start-Sleep -s 10
Scriptinzi
}
}
function Scriptinzi {
CheckCPU
CheckProcess }
Scriptinzi
I am trying to gather data from eventlogs of logons, disconnect, logoff etc... this data will be stored in a csv format.
This is the script i am working which got from Microsoft Technet and i have modified to meet my requirement. Script is working as it should be but there is looping going on which i can't figure out how it should be stopped.
$ServersToQuery = Get-Content "C:\Users\metho.HOME\Desktop\computernames.txt"
$cred = "home\Administrator"
$StartTime = "September 19, 2018"
#$Yesterday = (Get-Date) - (New-TimeSpan -Days 1)
foreach ($Server in $ServersToQuery) {
$LogFilter = #{
LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'
ID = 21, 23, 24, 25
StartTime = (Get-Date).AddDays(-1)
}
$AllEntries = Get-WinEvent -FilterHashtable $LogFilter -ComputerName $Server -Credential $cred
$AllEntries | Foreach {
$entry = [xml]$_.ToXml()
$Output += New-Object PSObject -Property #{
TimeCreated = $_.TimeCreated
User = $entry.Event.UserData.EventXML.User
IPAddress = $entry.Event.UserData.EventXML.Address
EventID = $entry.Event.System.EventID
ServerName = $Server
}
}
}
$FilteredOutput += $Output | Select TimeCreated, User, ServerName, IPAddress, #{Name='Action';Expression={
if ($_.EventID -eq '21'){"logon"}
if ($_.EventID -eq '22'){"Shell start"}
if ($_.EventID -eq '23'){"logoff"}
if ($_.EventID -eq '24'){"disconnected"}
if ($_.EventID -eq '25'){"reconnection"}
}
}
$Date = (Get-Date -Format s) -replace ":", "-"
$FilePath = "$env:USERPROFILE\Desktop\$Date`_RDP_Report.csv"
$FilteredOutput | Sort TimeCreated | Export-Csv $FilePath -NoTypeInformation
Write-host "Writing File: $FilePath" -ForegroundColor Cyan
Write-host "Done!" -ForegroundColor Cyan
#End
First time when i run the script, it runs fine and i get the csv output as it should be. When i run the script again than a new CSV is created (as it should be) but the same event log enteries are created twice and run it again than three enteries are created for the same event. This is very strange as a new csv is created each time and i dont not have -append switch for export-csv configured.
$FilteredOutput = #()
$Output = #()
I did try adding these two lines in above script as i read somewhere that it is needed if i am mixing multiple variables into a array (i do not understand this so applogies if i get this wrong).
Can someone please help me this, more importantly, I need to understand this as it is good to know for my future projects.
Thanks agian.
mEtho
It sounds like the$Output and $FilteredOutput variables aren't getting cleared when you run the script subsequent times (nothing in the current script looks to do that), so the results are just getting appended to these variables each time.
As you've already said, you could add these to the top of your script:
$FilteredOutput = #()
$Output = #()
This will initialise them as empty arrays at the beginning, which will ensure they start empty as well as make it possible for them to be appended to (which happens at the script via +=). Without doing this on the first run the script likely failed, so I assume you must have done this in your current session at some point for it to be working at all.
I have written a Do..Until statement to check whether a file exists. If the file is not there, it waits a couple seconds and checks again.
It is supposed to end when the file appears. I have tested by running the script without the file and then adding it in the folder as the script is running.
Instead of ending, it continues to loop endlessly. Can anyone see what I have missed?
$path = test-path "C:\Temp\test.txt"
do {
if (!($path)) {
Write-Host "Not here yet..."
Start-Sleep -s 3
}
} until($path)
Write-Host "Files here now"
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
The Test-Path statement is evaluated in the line
$path = Test-Path "C:\Temp\test.txt"
After that the variable $path contains the boolean result of the evaluation. Put the actual check in your conditions:
$path = "C:\Temp\test.txt"
do {
if (-not (Test-Path $path)) {
Write-Host "Not here yet..."
Start-Sleep -s 3
}
} until (Test-Path $path)
or define it as a function that you call in your conditions:
function Test-File {
Test-Path "C:\Temp\test.txt"
}
do {
if (-not (Test-File)) {
Write-Host "Not here yet..."
Start-Sleep -s 3
}
} until (Test-File)
OK, figured it out 3 minutes after posting this (and an hour before that of frustration!).
I needed to put the variable INSIDE the Do..Until statement. Like so:
do{
$path = test-path "C:\Temp\test.txt"
if (!($path))
{Write-Host "Not here yet..."
start-sleep -s 3}
}
until($path)
Write-Host "Files here now"
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
mode con: cols=52 lines=16
while ($script -ne "Q") {
$tag = ""
while (-not ($tag)) {
$tag = Read-Host 'Enter tag # or Q to quit'
$date = get-date -f MM-dd-yyyy_HH_mm_ss
$username = [Environment]::Username
if(!(Test-Path -path "C:\Users\$username\Desktop\PSTools\Screenshot Dump\")) {
New-Item "C:\Users\$username\Desktop\PSTools\Screenshot Dump\" -type directory
}
}
if ($tag -eq "Q"){break}
cls
#$ErrorActionPreference = 'silentlycontinue'
cd "C:\Users\$username\Desktop\PSTools"
set-alias psexec "C:\Users\$username\Desktop\PSTools\PsExec.exe"
set-alias nircmd "C:\Users\$username\Desktop\PSTools\nircmd.exe"
------>LOOP START HERE
psexec \\$tag -i -c -s nircmd savescreenshotfull "C:\$tag-$date.png"
move "\\$tag\c$\$tag-$date.png" "C:\Users\$username\Desktop\PSTools\Screenshot Dump\$tag-$date.png"
explorer "C:\Users\$username\Desktop\PSTools\Screenshot Dump\$tag-$date.png"
------>LOOP END HERE
"`n"
}
Basically I'm trying to loop where I have stated above, but I'm use to doing it in command prompt and it's not the same. I want to make it loop X amount of times or until I CTRL+C to quit it.
You could also use a Foreach loop.
Let's say you want to loop 5 times :
$NumberOfLoops = 5
Foreach ($loop in (1..$NumberOfLoops)) { Do Loopy Stuff }
You can use a basic FOR statement to loop. Although, PowerShell has some great looping techniques. It's worth a look through...
http://social.technet.microsoft.com/wiki/contents/articles/4542.powershell-loops.aspx
For an example of a basic FOR loop, just do as so (will loop 10 times ($x=0-9)):
FOR ($x=0; $x -lt 10; $x++) { DO LOOPY STUFF };
I was wondering if anyone could help with the following code shown. I'm basically trying to get this code re-hashed if possible to allow me to run it against a set of server names supplied in a text file named "servers.txt".
The DBCC should be run by the PS script and run against all DB for that servername. I'm not up to speed enough with PS to understand how to do this for this script.
How to change it allow to plug in values instead of being hardcoded for each servername?
I've read a bit around this and looked at the Invoke-Sql command which I believe is a SQL 2008 extension to PS.
Unfortunately the PS environment is from a SQL 2005 box and I dont have the power to get this moved so dont think ill be able to use invoke
Please see the original code and then my experiment to try and get it to run using invoke.
$ScriptName = $myInvocation.MyCommand.Name
[void][reflection.assembly]::LoadWithPartialName("System.Data.SqlClient")
$ConnString = "Server=DB-OCC05;Integrated Security=SSPI;Database=master;Application Name=$ScriptName"
$MasterConn = new-object ('System.Data.SqlClient.SqlConnection') $ConnString
$MasterCmd = new-object System.Data.SqlClient.SqlCommand
$MasterCmd.Connection = $MasterConn
$SqlDBCC = "DBCC CHECKDB(master) WITH TABLERESULTS"
$MasterCmd.CommandText = $SqlDBCC
$MasterConn.Open()
$Rset = $MasterCmd.ExecuteReader()
If ($Rset.HasRows -eq $true) {
While ($Rset.Read()) {
$line = $Rset["MessageText"]
If ($Rset["Level"] -gt 10) {
Write-Host $line -backgroundcolor Yellow -foregroundcolor Red
} else {
Write-Host $line
}
}
$Rset.Close()
}
$MasterConn.Close()
And then my test running from SQL 2005 environment:
Invoke-Sqlcmd -Query "SELECT GETDATE() AS TimeOfQuery;" -ServerInstance "MyComputer\MyInstance"
And also tried this test:
gc "C:\Powershell\Servers.txt" | foreach-object {Invoke-Sqlcmd "DBCC checkdb;" -ServerInstance "$_\MyInstance"}
But the above test runs didnt work cause of the:
The term 'Invoke-Sqlcmd' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path.
A few modifications to your script. Everything is basically the same except for the connection string and the few lines at the bottom for loading your servers.txt file (a text file with one line per instance) and enumerating its content:
function Execute-DBCC
{
param (
[parameter(Mandatory = $true)][string]$serverInstance
)
$connString = "Server=$serverInstance;Integrated Security=SSPI;Database=master;Application Name=$ScriptName"
$masterConn = new-object ('System.Data.SqlClient.SqlConnection') $connString
$masterCmd = new-object System.Data.SqlClient.SqlCommand
$masterCmd.Connection = $masterConn
$masterCmd.CommandText = "EXECUTE master.sys.sp_MSforeachdb 'DBCC CHECKDB([?]) WITH TABLERESULTS'"
$masterConn.Open()
$reader = $masterCmd.ExecuteReader()
if ($reader.HasRows -eq $true)
{
while ($reader.Read())
{
$messageText = $reader["MessageText"]
if ($reader["Level"] -gt 10)
{ Write-Host $messageText -backgroundcolor Yellow -foregroundcolor Red }
else
{ Write-Host $messageText }
}
$reader.Close()
}
$masterConn.Close()
}
[void][reflection.assembly]::LoadWithPartialName("System.Data.SqlClient")
$servers = #(Get-Content ".\servers.txt")
$servers | %{
Execute-DBCC $_
}