I have powershell code that takes an array, goes through each element and updates an SQL database on another server.
It seems to get stuck in the following code
do{
try{
$cmd_update = New-Object System.Data.SqlClient.SqlCommand($sql_update,$conn_update)
$found = $cmd_update.ExecuteNonQuery()
$num_moved=$num_moved+1
$timestamp = Get-Date
Add-Content D:\Script\Output_SCHOOLS_Default_Group.log "Moving $ip, SEP version $sep_version to $group_name, $timestamp`r"
Write-host Moving $ip, SEP version $sep_version to $group_name, $timestamp
$transactionComplete = $true
}
catch{
$transactionComplete = $false
}
} until ($transactionComplete)
It will stay in this look until $transactionComplete, which may never happen, depending on system bottlenecks.
How do I exit the look if 1 minute has passed and $transactionComplete is still false?
You can introduce one more condition that checks if the total time spent executing is more than one minute.
For example,
$MaxTime = new-timespan -Minutes 1
$StopWatch = [Diagnostics.StopWatch]::StartNew()
do{
try{
$cmd_update = New-Object System.Data.SqlClient.SqlCommand($sql_update,$conn_update)
$found = $cmd_update.ExecuteNonQuery()
$num_moved=$num_moved+1
$timestamp = Get-Date
Add-Content D:\Script\Output_SCHOOLS_Default_Group.log "Moving $ip, SEP version $sep_version to $group_name, $timestamp`r"
Write-host Moving $ip, SEP version $sep_version to $group_name, $timestamp
$transactionComplete = $true
}
catch{
$transactionComplete = $false
}
} until ($transactionComplete -or (-not $StopWatch.Elapsed))
Put this before the loop:
$start = Get-Date
And put this at the end of the catch block:
if (((Get-Date)-$start).TotalMinutes -gt 1) {
$transactionComplete = $true
}
Related
I am writing a script where it will only run during M-F 8AM-5PM. The problem is after it falls off either 8-5 or M-F while() loop it just... doesn't know how to get back into the loop. This makes me think that I may be approaching my script at the wrong angle. Maybe there's a better way of doing this.
The "do something" part is it's comparing file size between the same file at 5 minutes apart. This is for checking if the file is growing/shrinking or not.
# Declare current date/time
$Weekday = [int](Get-Date).DayOfWeek
$hour = [int](Get-Date -Format HH)
while (1 -eq 1) { # always true
$Weekday = [int](Get-Date).DayOfWeek
$hour = [int](Get-Date -Format HH)
while ($Weekday -ge 1 -and $Weekday -le 5) { # weekday
$Weekday = [int](get-date).DayOfWeek # loop check for current day
while ($hour -ge 8 -and $hour -le 16) { # 8AM-5PM
$hour = [int](get-date -format HH) # loop check for current hour
# Do Something
}
else {
# Sleep until next business hour
$date = Get-Date
$date = $date.AddDays(1)
$mmddyyy = $date.ToString("MM/dd/yyy")
$nextDy = New-TimeSpan -End "$mmddyyy 08:00"
Write-Host "Start sleep timer until next 8AM"
Start-Sleep -Seconds $nextDy.TotalSeconds
}
}
else {
# Sleep until next business day
$date = Get-Date
while ($Date.DayOfWeek -ne "Monday") {$date = $date.AddDays(1)}
$mmddyyy = $date.ToString("MM/dd/yyy")
$nextBu = New-TimeSpan -End "$mmddyyy 08:00"
Write-Host "Start sleep timer until next Monday 8AM"
Start-Sleep -Seconds $nextBu.TotalSeconds
}
}
Try using scheduled task or service.
If you want to use a PowerShell script try using this:
while ($true)
{
#WeekDay
$Weekday = [int](Get-Date).DayOfWeek #loop check for current day
$hour = [int](Get-Date -Format HH)
if ($Weekday -le 5 -and $Weekday -ge 1)
{
while ($hour -ge 8 -and $hour -le 16)
{
#8AM-5PM
$hour = [int](Get-Date -Format HH) #loop check for current hour
#Do Something
}
}
$date = Get-Date
$date = $date.AddDays(1)
$mmddyyy = $date.ToString("MM/dd/yyy")
$nextDy = New-TimeSpan -End "$mmddyyy 08:00"
Write-Host "Start sleep timer until next 8AM"
Start-Sleep -Seconds $nextDy.TotalSeconds
}
I think you got it totally wrong.
Better you use a simple library function like TestFileSizeUtil to monitor file size or use WMI like shown here:
$query = "Select * from __InstanceModificationEvent WITHIN 5 WHERE TargetInstance ISA 'CIM_DataFile' AND TargetInstance.Name='C:\\Logs\\test.log'"
Register-WmiEvent -Query $query -Action {
Write-Host "Current file size is: " $Event.SourceEventArgs.NewEvent.TargetInstance.FileSize
$prevSize = $Event.SourceEventArgs.NewEvent.PreviousInstance.FileSize
$curSize = $Event.SourceEventArgs.NewEvent.TargetInstance.FileSize
if ($curSize -gt $prevSize) {
$bytes = $curSize - $prevSize
Write-Host "File grew by: $bytes bytes"
} else {
$bytes = $prevSize - $curSize
Write-Host "File reduced by: $bytes bytes"
}
}
Then, it should not matter if your script runs all day long.
I'm writing a script that will watch a directory for any new mp4 files, then convert the file using HandBrake's CLI tool. The logic that watches the directory for changes works by itself, but if I drop a large video into the "watched" directory the conversion fails since it kicks off as soon as it sees a new file, before the file has time to finish copying.
I'm using a do until loop to check if the file is locked/downloading and then continues once the file is unlocked/writable. The loop works as a stand-alone script, but when used inside the filesystem watcher the script it will halt without any errors on this line:
[System.IO.FileStream] $fs = $convertingFile.OpenWrite();
This occurs regardless if $ErrorActionPreference = "SilentlyContinue" is commented out. I've been unable to gather any log output to see why the script is halting using Start-Transcript or Out-File.
How should I best gather error info about why the script halts once it hits this line?
Bonus: Why might this script not provide error information?
$ErrorActionPreference = "SilentlyContinue"
function Start-FileSystemWatcher {
[CmdletBinding()]
param(
[Parameter()]
[string]$Path,
[Parameter()]
[ValidateSet('Changed','Created','Deleted','Renamed')]
[string[]]$EventName,
[Parameter()]
[string]$Filter,
[Parameter()]
[System.IO.NotifyFilters]$NotifyFilter,
[Parameter()]
[switch]$Recurse,
[Parameter()]
[scriptblock]$Action
)
#region Build FileSystemWatcher
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
if (-not $PSBoundParameters.ContainsKey('Path')) {
$Path = $PWD
}
$FileSystemWatcher.Path = $Path
if ($PSBoundParameters.ContainsKey('Filter')) {
$FileSystemWatcher.Filter = $Filter
}
if ($PSBoundParameters.ContainsKey('NotifyFilter')) {
$FileSystemWatcher.NotifyFilter = $NotifyFilter
}
if ($PSBoundParameters.ContainsKey('Recurse')) {
$FileSystemWatcher.IncludeSubdirectories = $True
}
if (-not $PSBoundParameters.ContainsKey('EventName')) {
$EventName = 'Changed','Created','Deleted','Renamed'
}
if (-not $PSBoundParameters.ContainsKey('Action')) {
$Action = {
switch ($Event.SourceEventArgs.ChangeType) {
'Renamed' {
$Object = "{0} was {1} to {2} at {3}" -f $Event.SourceArgs[-1].OldFullPath,
$Event.SourceEventArgs.ChangeType,
$Event.SourceArgs[-1].FullPath,
$Event.TimeGenerated
}
Default {
$Object = "{0} was {1} at {2}" -f $Event.SourceEventArgs.FullPath,
$Event.SourceEventArgs.ChangeType,
$Event.TimeGenerated
}
}
$WriteHostParams = #{
ForegroundColor = 'Green'
BackgroundColor = 'Black'
Object = $Object
}
Write-Host #WriteHostParams
}
}
$ObjectEventParams = #{
InputObject = $FileSystemWatcher
Action = $Action
}
foreach ($Item in $EventName) {
$ObjectEventParams.EventName = $Item
$ObjectEventParams.SourceIdentifier = "File.$($Item)"
Write-Verbose "Starting watcher for Event: $($Item)"
$Null = Register-ObjectEvent #ObjectEventParams
}
}
$FileSystemWatcherParams = #{
Path = 'X:\share\scripts\ps\converter\input'
Recurse = $True
NotifyFilter = 'FileName'
Verbose = $True
Action = {
$Item = Get-Item $Event.SourceEventArgs.FullPath
$WriteHostParams = #{
ForegroundColor = 'Green'
BackgroundColor = 'Black'
}
$inputFile = "${PWD}\input\$($Item.Name)".trim()
$outputFile = "${PWD}\output\$($Item.Name)".trim()
$logFile = "${PWD}\log\$($Item.Name).txt"
$testLogFile = "${PWD}\log\$($Item.Name)(t).txt"
function mp4-Func {
Start-Transcript -path $logFile
Write-Host "New mp4 file detected..."
$convertingFile = New-Object -TypeName System.IO.FileInfo -ArgumentList $inputFile
$locked = 1
do {
[System.IO.FileStream] $fs = $convertingFile.OpenWrite();
if (!$?) {
Write-Host "Can't convert yet, file appears to be loading..."
sleep 2
}
else {
$fs.Dispose()
$locked = 0
}
} until ($locked -eq 0)
Write-Host "File unlocked and ready for conversion."
HandBrake
Stop-Transcript
$WriteHostParams.Object = "Finished converting: $($Item.Name)"
}
function HandBrake {
.\HandBrakeCLI.exe --input "$inputFile" `
--output "$outputFile" `
--format av_mp4 `
--encoder x264 `
--vb 1700 `
--two-pass `
--aencoder copy:aac `
--ab 320 `
--arate 48 `
--mixdown stereo
}
switch -regex ($Item.Extension) {
'\.mp4' { mp4-Func }
}
Write-Host #WriteHostParams
}
}
#( 'Created') | ForEach-Object {
$FileSystemWatcherParams.EventName = $_
Start-FileSystemWatcher #FileSystemWatcherParams
}
I think you'll find that $ErrorActionPreference only impact errors at the level of cmdlets, while you hitting a problem in non-cmdlet code. For that, you'll probably need a try/catch construct.
Based on Burt_Harris' answer (please upvote his answer over this one), I changed the "do while" loop so that it would use try/catch rather than an if/else statement. By using $.Exception.Message and $.Exception.ItemName I was able to better understand why the script was halting at that particular line.
Working code:
Do {
Try {
[System.IO.FileStream] $fs = $convertingFile.OpenWrite()
$fs.Dispose()
$locked = 0
}
Catch {
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
Write-Host $ErrorMessage
Write-Host $FailedItem
Write-Host "Can't convert yet, file appears to be loading..."
sleep 5
continue
}
} until ($locked -eq 0)
I am trying to create a thread that will use a shared variable (between main session and the thread)
plus the give the thread ability to use outside function from the main code
I have managed to pass the function for the thread to use
and i have managed to pass read only variable.
my problem is that if i am changing the value of the variable inside the thread and then i try to read it from the main session - i can not see the change in the value, therefore its not shared.
How do i do that?
My goal is to have one thread in the end.
this is my code:
$x = [Hashtable]::Synchronized(#{})
$global:yo = "START"
Function ConvertTo-Hex {
#Write-Output "Function Ran"
write-host "hi"
$x.host.ui.WriteVerboseLine("===========")
write-host $yo
$yo="TEST"
write-host $yo
}
#endregion
$rs = [RunspaceFactory]::CreateRunspace()
$rs.ApartmentState,$rs.ThreadOptions = "STA","ReUseThread"
$rs.Open()
$rs.SessionStateProxy.SetVariable("x",$x)
ls
# create an array and add it to session state
$arrayList = New-Object System.Collections.ArrayList
$arrayList.AddRange(('a','b','c','d','e'))
$x.host = $host
$sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$sessionstate.Variables.Add((New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry('arrayList', $arrayList, $null)))
$sessionstate.Variables.Add((New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry('x', $x, $null)))
#$sessionstate.Variables.Add((New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry('yo', $yo, $true)))
$sessionstate.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'ConvertTo-Hex', (Get-Content Function:\ConvertTo-Hex -ErrorAction Stop)))
$runspacepool = [runspacefactory]::CreateRunspacePool(1, 2, $sessionstate, $Host)
$runspacepool.Open()
$ps1 = [powershell]::Create()
$ps1.RunspacePool = $runspacepool
$ps1.AddScript({
for ($i = 1; $i -le 15; $i++)
{
$letter = Get-Random -InputObject (97..122) | % {[char]$_} # a random lowercase letter
$null = $arrayList.Add($letter)
start-sleep -s 1
}
})
$ps1.AddParameter(#($yo))
# on the first thread start a process that adds values to $arrayList every second
$handle1 = $ps1.BeginInvoke()
# now on the second thread, output the value of $arrayList every 1.5 seconds
$ps2 = [powershell]::Create()
$ps2.RunspacePool = $runspacepool
$ps2.AddScript({
Write-Host "ArrayList contents is "
foreach ($i in $arrayList)
{
Write-Host $i -NoNewline
Write-Host " " -NoNewline
}
Write-Host ""
$yo = "BAH"
ConvertTo-Hex
})
$ps2.AddParameter(#($yo))
1..10 | % {
$handle2 = $ps2.BeginInvoke()
if ($handle2.AsyncWaitHandle.WaitOne())
{
$ps2.EndInvoke($handle2)
}
start-sleep -s 1.5
write-host "====================" + $yo
}
Here is my Powershell code that when i execute it, it will continually loop while waking up my internal sites
$urlFile = "C:\Users\lfouche\Desktop\url\url.txt"
foreach($site in Get-Content $urlFile){
function WakeUp([string] $url)
{
Write-Host "Waking up $url ..." -NoNewLine
$client = new-object system.net.WebClient
$client.UseDefaultCredentials = $true
$null = $client.OpenRead($url)
$client.Dispose()
Write-Host " Ok"
}
#(
Get-Content $urlFile
) | % { WakeUp $_ }
}
Well, your script can generally be improved, however I think the error is that you already iterate over Get-Content $urlFile within your foreach condition and also in the loop. Try this:
# define the wake up function once
function WakeUp
{
Param
(
[string] $url
)
Write-Host "Waking up $url ..." -NoNewLine
$client = new-object system.net.WebClient
$client.UseDefaultCredentials = $true
$null = $client.OpenRead($url)
$client.Dispose()
Write-Host " Ok"
}
$urlFile = "C:\Users\lfouche\Desktop\url\url.txt"
$sites = Get-Content $urlFile
foreach($site in $sites)
{
# call wake up for each site
WakeUp $site
}
I'm new to powershell. I read some lines on www.powershell.com. Now I need your help to solve a problem. I want to read the UUID from clients in the Network. Therefore I created a document "pcs.txt" where all PCs are stored.
$pc = Get-Content pcs.txt #Read content of file
$cred = Get-Credential “domain\user”
for ($i=0; $i -lt $pc.length; $i++) {
$Result=test-connection -ComputerName $pc[$i] -Count 1 -Quiet
If ($Result -eq 'True')
{
$uuid = (Get-WmiObject Win32_ComputerSystemProduct -ComputerName $pc[$i] -Credential $cred).UUID
$Ausgabe=$pc[$i] + ';'+$uuid
$Ausgabe
}
else
{
$Ausgabe=$pc[$i] + '; UUID nicht erhalten'
$Ausgabe
}
}
First I test if the ping works. When the ping works I try to get the uuid.
Sometimes I don't get the uuid even if the ping worked. So I would like to code a timeout, which say -> go to next pc when you don't have the uuid after 2 seconds.
Can you help me please?
Alas, there is no timeout parameter for Get-WmiObject commandlet. There is a feature request in MS Connect, but it is from 2011 and still open.
A workaround, which I haven't tested is available by using System.Management. I'll copy-and-paste it here in case the link goes dead. (And I hate SO answers that only contain links to resouces that may or may not exist...)
Function Get-WmiCustom([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15){
$ConnectionOptions = new-object System.Management.ConnectionOptions
$EnumerationOptions = new-object System.Management.EnumerationOptions
$timeoutseconds = new-timespan -seconds $timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $computername + "\" + $namespace
#write-host $assembledpath -foregroundcolor yellow
$Scope = new-object System.Management.ManagementScope $assembledpath, $ConnectionOptions
$Scope.Connect()
$querystring = "SELECT * FROM " + $class
#write-host $querystring
$query = new-object System.Management.ObjectQuery $querystring
$searcher = new-object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope
trap { $_ } $result = $searcher.get()
return $result
}
I found a good workaround!
http://theolddogscriptingblog.wordpress.com/2012/05/11/wmi-hangs-and-how-to-avoid-them/
Here my working code:
$pc = Get-Content pcs.txt #FILE FROM THE HARDDISK
$cred = Get-Credential “DOMAIN\USER” #
for ($i=0; $i -lt $pc.length; $i++)
{
$Result=test-connection -ComputerName $pc[$i] -Count 1 -Quiet
If ($Result -eq 'True')
{
$WMIJob = Get-WmiObject Win32_ComputerSystemProduct -ComputerName $pc[$i] -Credential $cred -AsJob
$Timeout=Wait-Job -ID $WMIJob.ID -Timeout 1 # the Job times out after 1 seconds.
$uuid = Receive-Job $WMIJob.ID
if ($uuid -ne $null)
{
$Wert =$uuid.UUID
$Ausgabe=$pc[$i] + ';'+$Wert
$Ausgabe
}
else
{
<#$b = $error | select Exception
$E = $b -split (:)
$x = $E[1]
$Error.Clear() #>
$Ausgabe=$pc[$i] + '; got no uuid'
$Ausgabe
}
}
else
{
$Ausgabe='PC not reached through ping.'
$Ausgabe
}
}
I hope I can help somebody with that