Всем доброго времени суток, хочу поделиться простой инструкцией «Как можно перестать вручную пинговать десяток хостов. Без регистрации и СМС!».

С просторов Интернета

Важно: я не программист и не системный администратор, но мне приходиться работать одновременно с большим набором серверов и сервисов и данный скрипт я написал для собственного удобства.

Всех кому это может быть полезно любопытно, прошу под кат.


Почему Powershell
У меня есть опыт написания простых программ на Python, но он требует либо установленного интерпретатора, либо права на запуск .exe, что, в рамках моей работы не всегда возможно. А вот скрипт на Powershell чаще находит возможность запустить.

Не все любят консоль/терминал


Думаю в этом нет противоречия, в противном случае все бы до сих пользовались консольными почтовыми клиентами и в *nix-системах не появился GUI.

Поскольку в повседневности мне приходится использовать Powershell достаточно примитивно, то для начала я попытался понять, а можно ли к нему прикрутить GUI. Оказывается можно, да еще и достаточно просто:


Add-Type -assembly System.Windows.Forms #Подключим библиотеку
$main_form = New-Object System.Windows.Forms.Form #Создаем объект с нашим "окном"

#Задаем для него параметры
$main_form.Text ='Links up' #Имя в заголовке
$main_form.Width = 300 #Ширина окна
$main_form.Height = 200 #Высота окна
$main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости
$main_form.ShowDialog() #Вызываем "окно"

Добавляем к этому элемент вывода и получаем элементарное Windwows-окно тавтология?.


Add-Type -assembly System.Windows.Forms #Подключим библиотеку

$main_form = New-Object System.Windows.Forms.Form #Создаем переменную с нашим "окном"

#Задаем для него параметры через параметры
$main_form.Text ='Links up' #Имя в заголовке
$main_form.Width = 300 #Ширина окна
$main_form.Height = 200 #Высота окна
$main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости

$Label = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell
$Label.Text = "Привет! Я элементарное Windows-окно, а чего добился ты?" #Выводимый текст
$Label.Location  = New-Object System.Drawing.Point(10,65) #Расположение объекта внутри формы по горизонтали и вертикали (x,y)
$Label.AutoSize = $true 

$main_form.Controls.Add($Label) #добавляем созданный объект к форме, чтобы он не потерялся при выполнении скрипта

$main_form.ShowDialog() #Вызываем оскорбительно элементарное окно

Добавим динамики


Так инструмент которым я «пинговал Яндекс» и монтировал *.iso обзавёлся обособленным GUI, но этого мало. Подобным образом можно вывести информацию о доступности, но для мониторинга придется каждый раз перезапускать скрипт.

Для начала нарисуем две иконки и добавим возможность выводить изображения.


$PictureBox = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox.width = 10
$PictureBox.height = 10
$PictureBox.location = New-Object System.Drawing.Point(178,12)

#Вот тут мы переходим к делу привязываем выводимую картинку к результату проверки доступности узла используя командлет
#<b>test-connection</b>, где:
#-Count - передает кол-во запросов
#-computer - IP или доменное имя для запроса
#-quiet - для того чтобы нам вернулось только Boolean значение
if ((test-connection -Count 1 -computer ya.ru -quiet) -eq $True) {
	$PictureBox.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$main_form.Controls.Add($PictureBox)

Опять наши DNS-ники шалят...


И еще добавим кнопку, чтобы была возможность обновлять показания.


$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(100,150) 
$Button.Size = New-Object System.Drawing.Size(80,30)
$Button.Text = "Reload"

# Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике
$Button.Add_Click({ 
	if ((Test-Connection -Count 1 -computer ya.ru -quiet) -eq $True)
		{$PictureBox.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox.imageLocation	= "C:\Test\no.png"}
	  
	if ((Test-Connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True)
		{$PictureBox1.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox1.imageLocation	= "C:\Test\no.png"}
 	})

$main_form.Controls.Add($Button)


Чудесно, теперь в продакшн!?


Вручную прописав около полудюжины хостов я вдруг понял одну вещь — а ведь этот проект закончиться, хосты поменяют и мне придется каждый раз править скрипт. С одной стороны ничего страшного, это же решение для себя любимого. С другой стороны это решение для себя любимого вы взгляните на то что получается:

Зрелище не для слабонервных
Add-Type -assembly System.Windows.Forms #Подключим библиотеку

$main_form = New-Object System.Windows.Forms.Form #Создаем переменную с нашим "окном"

#Задаем для него параметры через параметры
$main_form.Text ='Links up' #Имя в заголовке
$main_form.Width = 300 #Ширина окна
$main_form.Height = 200 #Высота окна
$main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости

$Label = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell
$Label.Text = "ya.ru		............................" #Выводимый текст 
$Label.Location  = New-Object System.Drawing.Point(15,10) #Расположение объекта внутри формы по горизонтали и вертикали (x,y)
$Label.AutoSize = $true 

$Label1 = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell
$Label1.Text = "8.8.8.8		............................" #Выводимый текст googl-овский DNS-ник
$Label1.Location  = New-Object System.Drawing.Point(15,30) #Расположение объекта внутри формы по горизонтали и вертикали (x,y)
$Label1.AutoSize = $true 

$Label2 = New-Object System.Windows.Forms.Label
$Label2.Text = "192.168.x.x	............................"
$Label2.Location  = New-Object System.Drawing.Point(15,50)
$Label2.AutoSize = $true

$Label3 = New-Object System.Windows.Forms.Label
$Label3.Text = "192.168.x.x	............................"
$Label3.Location  = New-Object System.Drawing.Point(15,70)
$Label3.AutoSize = $true

$Label4 = New-Object System.Windows.Forms.Label
$Label4.Text = "10.0.x.x	 ............................"
$Label4.Location  = New-Object System.Drawing.Point(15,90)
$Label4.AutoSize = $true

$Label5 = New-Object System.Windows.Forms.Label
$Label5.Text = "162.102.x.x	 ............................"
$Label5.Location  = New-Object System.Drawing.Point(15,110)
$Label5.AutoSize = $true

$PictureBox = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox.width = 10
$PictureBox.height = 10

#Вот тут мы переходим к делу привязываем выводимую картинку к результату проверки доступности узла используя командлет
#<b>test-connection</b>, где:
#-Count - передает кол-во запросов
#-computer - IP или доменное имя для запроса
#-quiet - для того чтобы нам вернулось только Boolean значение
$PictureBox.location = New-Object System.Drawing.Point(235,12)
if ((test-connection -Count 1 -computer ya.ru -quiet) -eq $True) {
	$PictureBox.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox1 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox1.width = 10
$PictureBox1.height = 10
$PictureBox1.location = New-Object System.Drawing.Point(235,32)
if ((test-connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True) {
	$PictureBox1.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox1.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox1.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox2 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox2.width = 10
$PictureBox2.height = 10
$PictureBox2.location = New-Object System.Drawing.Point(235,52)
if ((test-connection -Count 1 -computer 192.168.x.x -quiet) -eq $True) {
	$PictureBox2.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox2.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox2.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox3 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox3.width = 10
$PictureBox3.height = 10
$PictureBox3.location = New-Object System.Drawing.Point(235,72)
if ((test-connection -Count 1 -computer 192.168.x.x -quiet) -eq $True) {
	$PictureBox3.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox3.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox3.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox4 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox4.width = 10
$PictureBox4.height = 10
$PictureBox4.location = New-Object System.Drawing.Point(235,92)
if ((test-connection -Count 1 -computer 10.0.x.x -quiet) -eq $True) {
	$PictureBox4.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox4.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox1.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox5 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox5.width = 10
$PictureBox5.height = 10
$PictureBox5.location = New-Object System.Drawing.Point(235,112)
if ((test-connection -Count 1 -computer 162.102.x.x -quiet) -eq $True) {
	$PictureBox5.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
	}
Else {
	$PictureBox5.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
	}
$PictureBox5.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(100,150) 
$Button.Size = New-Object System.Drawing.Size(80,30)
$Button.Text = "Reload"

# Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике
$Button.Add_Click({ 
	if ((Test-Connection -Count 1 -computer ya.ru -quiet) -eq $True)
		{$PictureBox.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox.imageLocation	= "C:\Test\no.png"}
	  
	if ((Test-Connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True)
		{$PictureBox1.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox1.imageLocation	= "C:\Test\no.png"}

	if ((Test-Connection -Count 1 -computer 192.168.x.x -quiet) -eq $True)
		{$PictureBox2.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox2.imageLocation	= "C:\Test\no.png"}

	if ((Test-Connection -Count 1 -computer 192.168.x.x -quiet) -eq $True)
		{$PictureBox3.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox3.imageLocation	= "C:\Test\no.png"}

	if ((Test-Connection -Count 1 -computer 10.0.x.x -quiet) -eq $True)
		{$PictureBox4.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox4.imageLocation	= "C:\Test\no.png"}

	if ((Test-Connection -Count 1 -computer 162.102.x.x -quiet) -eq $True)
		{$PictureBox5.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox5.imageLocation	= "C:\Test\no.png"}
	})

$main_form.Controls.Add($Label) #добавляем созданный объект к форме, чтобы он не потерялся при выполнении скрипта
$main_form.Controls.Add($Label1)
$main_form.Controls.Add($Label2)
$main_form.Controls.Add($Label3)
$main_form.Controls.Add($Label4)
$main_form.Controls.Add($Label5)
$main_form.Controls.Add($PictureBox)
$main_form.Controls.Add($PictureBox1)
$main_form.Controls.Add($PictureBox2)
$main_form.Controls.Add($PictureBox3)
$main_form.Controls.Add($PictureBox4)
$main_form.Controls.Add($PictureBox5)
$main_form.Controls.Add($Button)

$main_form.ShowDialog() #Вызываем оскорбительно элементарное окно

Даже если соблюдать чистоту и придерживаться структуры то периодическое редактирование будет вызывать не самые приятные ощущения.

Для начала выносим проверку доступности в отдельную функцию, а путь к каталогу в отдельную переменную:

$script_path	= "C:\Test\PS ping"

#Переезжаем в финкцию уже созданный объект PictureBox и IP-адресс который нужно проверить.
#На выходе оно меняет подгружаемую картинку.
function get_status($PictureBox,$path_ip) {
	if ((Test-Connection -Count 1 -computer $path_ip -quiet) -eq $True)
		{$PictureBox.imageLocation	= $script_path + "\yes.png"}
	Else {$PictureBox.imageLocation	= $script_path + "\no.png"} 
	}

Вынесем данные о хосте во внешний файл path.txt, записывая в виде «ip/host-functional-name/», а в документе будем принимать их в массивы. Так же сделаем расчет положения по Y более автоматизированным. Все это дает нам возможность создавать Label`ы и checkbox`ы вызовом функции:


#создаем описательную линию, на вход идут: объект Label, IP-адресс, описание и позиция по оси Y
Function Create_line($label,$path_ip,$caption, $top){ 
	$label.Location = New-Object System.Drawing.Point(1, $top)
	$label.text = $path_ip+$caption
	$label.font = $font
	$Label.AutoSize = $true
	}
#создаем индикатор для описательной линию, на вход идут: объект PictureBox, IP-адресс и позиция по оси Y
Function Create_link($PictureBox,$path_ip, $top){ 
	$PictureBox.width	= 10
	$PictureBox.height	= 10
	$PictureBox.location	= New-Object System.Drawing.Point(210,$top)
	get_status -PictureBox $PictureBox -path_ip $path_ip
	$PictureBox.SizeMode	= [System.Windows.Forms.PictureBoxSizeMode]::zoom
	}

$line = Get-content -LiteralPath $script_path"\path.txt" #вытаскиваем данные о необходимых хостах
$len = $line.Length	#вычисляем длину, а точнее массив длин каждой строки

$i = 0

#далее объявляем массивы которые понадобятся дальше
$ip = @()	#массив ip
$capt = @()	#массив описания
$Labels =@()	#массив Label
$PictureBoxs =@()	#массив PictureBox

while($i -lt $len){
	$f = $line[$i].IndexOf("/") #вычисляем позицию начала описания
	$l = $line[$i].LastIndexOf("/")	#вычисляем позицию конца описания
	$ip += $line[$i].Substring(0,$f)	#вытаскиваем ip
	$capt += $line[$i].Substring($f+1,$l-$f-1)	#вытаскиваем описание

	#создаем поле Label
	Create_line -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ip $ip[$i] -caption $capt[$i] -top $label_from_top
	#создаем поле PictureBox
	Create_link -PictureBox ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ip $ip[$i] -top $label_from_top

	$label_from_top += 15	#делаем "шаг" по оси Y

	$Labels	+= $label_obj	#вносим созданный Label в общий массив
	$PictureBoxs	+= $PictureBox_obj	#вносим созданный PictureBox в общий массив
	$main_form.Controls.Add($Labels[$i])
	$main_form.Controls.Add($PictureBoxs[$i])
	$i	+=1
	}

Добавим место для вывода времени последней проверки, потому что это удобно, и немого дополним действие при нажатии на кнопку.


$Label0	= New-Object System.Windows.Forms.Label
$Label0.Text	= Get-Date
$Label0.Location	= New-Object System.Drawing.Point(80,180)
$Label0.AutoSize	= $true
$main_form.Controls.Add($Label0)
$Button.Add_Click({ 
	while($i -lt $len){
		get_status -PictureBox $PictureBoxs[$i] -path_ip $ip[$i]
		$i +=1
		}
	$Label0.Text	= Get-Date 
	})

А так же я присоединил небольшую фитчуне баг, которая при обнаружении недоступного хоста сообщало не только изменением индикатора, но и вопило из динамиков.

Add-Type -AssemblyName System.Speech
$voice	= New-Object System.Speech.Synthesis.SpeechSynthesizer
$voice.Rate	= 5
$voice.Speak("Ворнинг! Ворнинг! Байз из андер аттак!")

Так что после нескольких украшательств и приведения к читабельномуудобному мне виду, получилось вот так:

Полный код скрипта
Add-Type -AssemblyName System.Speech
Add-Type -assembly System.Windows.Forms

$script_path	= "C:\PS ping"
$label_from_top	= 10
$voice	= New-Object System.Speech.Synthesis.SpeechSynthesizer
$voice.Rate	= 5

#Переезжаем в финкцию уже созданный объект PictureBox и IP-адресс который нужно проверить.
#На выходе оно меняет подгружаемую картинку.
function get_status($PictureBox,$path_ip) {
	if ((Test-Connection -Count 1 -computer $path_ip -quiet) -eq $True) {
		$PictureBox.imageLocation	= $script_path + "\yes.png"
		}
	Else {
		$PictureBox.imageLocation	= $script_path + "\no.png"
		$voice.Speak("Ошибка! Хост " + $path_ip + ", недоступен!")
		}
	}

#создаем описательную линию, на вход идут: объект Label, IP-адресс, описание и позиция по оси Y
Function Create_line($label,$path_ip,$caption, $top){ 
	$label.Location	= New-Object System.Drawing.Point(1, $top)
	$label.text	= $path_ip+$caption
	$label.font	= $font
	$Label.AutoSize	= $true
	}

#создаем индикатор для описательной линию, на вход идут: объект PictureBox, IP-адресс и позиция по оси Y
Function Create_link($PictureBox,$path_ip, $top){ 
	$PictureBox.width	= 10
	$PictureBox.height	= 10
	$PictureBox.location	= New-Object System.Drawing.Point(210,$top)
	get_status -PictureBox $PictureBox -path_ip $path_ip
	$PictureBox.SizeMode	= [System.Windows.Forms.PictureBoxSizeMode]::zoom
	}

$main_form	= New-Object System.Windows.Forms.Form
$main_form.Text ='Links up' #Имя в заголовке
$main_form.Width = 300 #Ширина окна
$main_form.Height = 200 #Высота окна
$main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости
$main_form.TopMost	= $true #Это очень важная штука, она дает возможность закрепить окно поверх других

$line = Get-content -LiteralPath $script_path"\path.txt" #вытаскиваем данные о необходимых хостах
$len = $line.Length	#вычисляем длину, а точнее массив длин каждой строки

$i = 0

#далее объявляем массивы которые понадобятся дальше
$ip	= @()	#массив ip
$capt	= @()	#массив описания
$Labels	=@()	#массив Label
$PictureBoxs	=@()	#массив PictureBox

while($i -lt $len){
	$f	= $line[$i].IndexOf("/") #вычисляем позицию начала описания
	$l	= $line[$i].LastIndexOf("/")	#вычисляем позицию конца описания
	$ip	+= $line[$i].Substring(0,$f)	#вытаскиваем ip
	$capt	+= $line[$i].Substring($f+1,$l-$f-1)	#вытаскиваем описание

	#создаем поле Label
	Create_line -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ip $ip[$i] -caption $capt[$i] -top $label_from_top
	#создаем поле PictureBox
	Create_link -PictureBox ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ip $ip[$i] -top $label_from_top

	$label_from_top	+= 15	#делаем "шаг" по оси Y

	$Labels	+= $label_obj	#вносим созданный Label в общий массив
	$PictureBoxs	+= $PictureBox_obj	#вносим созданный PictureBox в общий массив
	$main_form.Controls.Add($Labels[$i])
	$main_form.Controls.Add($PictureBoxs[$i])
	$i	+=1
}

#это поле с последним временем проверки
$Label0	= New-Object System.Windows.Forms.Label
$Label0.Text	= Get-Date
$Label0.Location	= New-Object System.Drawing.Point(80,180)
$Label0.AutoSize	= $true


# Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике
$Button	= New-Object System.Windows.Forms.Button
$Button.Location	= New-Object System.Drawing.Size(100,200)
$Button.Size	= New-Object System.Drawing.Size(80,30)
$Button.Text	= "Reload"

$Button.Add_Click({ 
	while($i -lt $len){
		get_status -PictureBox $PictureBoxs[$i] -path_ip $ip[$i]
		$i	+=1
		}
	$i	= 0
	$Label0.Text	= Get-Date 
})

$main_form.Controls.Add($Button)
$main_form.Controls.Add($Label0)

$main_form.ShowDialog()


Послесловие


Надеюсь кому-то данный туториал будет полезен. Если же вы такое чуть-ли не с пеленок делали, то поздравляю, я — не делал. Но я готов выслушать ваши комментарии и предложения.

Для меня самого было приятно покопаться и собрать вот такую небольшую, но полезную в быту утилиту. В планах еще стоит реализовать добавления и удаления узлов из GUI, а так же сделать таки то что должно было быть реализованно в первую очередь автоматическое обновление статусов без необходимости «тыкать» кнопку и чтобы оно не вешало «powershell.exe».

Последняя версия скрипта на GitHub

Комментарии (16)


  1. censor2005
    15.08.2019 14:02

    «Я не программист», говорите? По-моему, именно такие утилиты выдают программиста )


    1. AlexUl Автор
      15.08.2019 14:15

      Программисты пишут сами для себя утилиты? =)
      Мне казалось это удел всех, кто в какой-то момент понимает что нужна автоматизация.


  1. IDDQDesnik
    15.08.2019 14:08

    Не работает на Windows 7 @ Powershell 2.0


    1. AlexUl Автор
      15.08.2019 14:12

      Код не идеален и проверялся только на PSversion 4.0 и 5.1, под Win10, WinServ 2012 и WinServ 2008.

      Можете поделиться, на что жалуется?


      1. IDDQDesnik
        15.08.2019 14:15
        +1

        Заголовок спойлера
        PS C:\PS ping> C:\PS ping\script.ps1
        Произошла ошибка при вызове метода, так как [System.Object[]] не содержит метод с именем "Substring".
        C:\PS ping\script.ps1:42 знак:27
        + $line = $data.Substring <<<< (0,47)
        + CategoryInfo : InvalidOperation: (Substring:String) [], RuntimeException
        + FullyQualifiedErrorId : MethodNotFound

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:51 знак:16
        + $f = $line[ <<<< $i].IndexOf("/")
        + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:52 знак:16
        + $l = $line[ <<<< $i].LastIndexOf("/")
        + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:53 знак:18
        + $ip += $line[ <<<< $i].Substring(0,$f)
        + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:54 знак:19
        + $capt += $line[ <<<< $i].Substring($f+1,$l-$f-1)
        + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Test-Connection : Не удается проверить аргумент для параметра "ComputerName". Аргумент пустой или имеет значение NULL. Укажите не пустой аргумент, не имеющий значение NULL, после чего повторите выполнение команды.
        C:\PS ping\script.ps1:10 знак:41
        + if ((Test-Connection -Count 1 -computer <<<< $path_ip -quiet) -eq $True) {
        + CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:51 знак:16
        + $f = $line[ <<<< $i].IndexOf("/")
        + CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:52 знак:16
        + $l = $line[ <<<< $i].LastIndexOf("/")
        + CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:53 знак:18
        + $ip += $line[ <<<< $i].Substring(0,$f)
        + CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:54 знак:19
        + $capt += $line[ <<<< $i].Substring($f+1,$l-$f-1)
        + CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Test-Connection : Не удается проверить аргумент для параметра "ComputerName". Аргумент пустой или имеет значение NULL. Укажите не пустой аргумент, не имеющий значение NULL, после чего повторите выполнение команды.
        C:\PS ping\script.ps1:10 знак:41
        + if ((Test-Connection -Count 1 -computer <<<< $path_ip -quiet) -eq $True) {
        + CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:51 знак:16
        + $f = $line[ <<<< $i].IndexOf("/")
        + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:52 знак:16
        + $l = $line[ <<<< $i].LastIndexOf("/")
        + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:53 знак:18
        + $ip += $line[ <<<< $i].Substring(0,$f)
        + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Не удается индексировать в массив NULL.
        C:\PS ping\script.ps1:54 знак:19
        + $capt += $line[ <<<< $i].Substring($f+1,$l-$f-1)
        + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Test-Connection : Не удается проверить аргумент для параметра "ComputerName". Аргумент пустой или имеет значение NULL. Укажите не пустой аргумент, не имеющий значение NULL, после чего повторите выполнение команды.
        C:\PS ping\script.ps1:10 знак:41
        + if ((Test-Connection -Count 1 -computer <<<< $path_ip -quiet) -eq $True) {
        + CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand




        1. AlexUl Автор
          15.08.2019 14:45

          Вижу что не находит метод «Substring», но с чем это связано: мой код, версия ОС или версия Powershell — не могу сказать. Может кто-то с большим опытом сможет указать на причину.

          К сожалению, сейчас нет возможности протестировать в похожих условиях, но как только появится — обязательно проверю и отпишусь.


      1. IDDQDesnik
        15.08.2019 15:24

        И еще вдогонку:

        Windows server 2016 @ Powershell 5
        Запускается, выдавая первую ошибку. После нажатия Reload выдает 2 и 3.

        Windows PowerShell
        (C) Корпорация Майкрософт (Microsoft Corporation), 2016. Все права защищены.

        PS > $PSVersionTable

        Name Value
        ---- -----
        PSVersion 5.1.14393.3053
        PSEdition Desktop
        PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
        BuildVersion 10.0.14393.3053
        CLRVersion 4.0.30319.42000
        WSManStackVersion 3.0
        PSRemotingProtocolVersion 2.3
        SerializationVersion 1.1.0.1

        PS C:\PS ping> .\script.ps1
        Исключение при вызове "Speak" с "1" аргументами: "Обнаружена ошибка звукового устройства. - Error Code: 0x2"
        C:\PS ping\script.ps1:15 знак:3
        + $voice.Speak("Ошибка! Хост " + $path_ip + ", недоступен!")
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : AudioException

        Не удается найти свойство "imageLocation" для данного объекта. Убедитесь, что оно существует и его можно задать.
        C:\PS ping\script.ps1:11 знак:3
        + $PictureBox.imageLocation = $script_path + "\yes.png"
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : InvalidOperation: (:) [], RuntimeException
        + FullyQualifiedErrorId : PropertyNotFound

        Исключение при вызове "Speak" с "1" аргументами: "Операция является недопустимой из-за текущего состояния объекта."
        C:\PS ping\script.ps1:15 знак:3
        + $voice.Speak("Ошибка! Хост " + $path_ip + ", недоступен!")
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : InvalidOperationException

        Cancel


        1. AlexUl Автор
          15.08.2019 15:34

          Исключение при вызове «Speak» с «1» аргументами: «Обнаружена ошибка звукового устройства. — Error Code: 0x2»
          C:\PS ping\script.ps1:15 знак:3
          + $voice.Speak(«Ошибка! Хост » + $path_ip + ", недоступен!")
          + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo: NotSpecified: (:) [], MethodInvocationException
          + FullyQualifiedErrorId: AudioException
          Первое логично, на серверах редко подразумевается наличие динамика.

          Add-Type: Cannot find path 'C:\PS ping\System.Windows.Forms.dll' because it does not exist.
          At C:\PS ping\script.ps1:2 char:1
          + Add-Type -assembly System.Windows.Forms
          + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo: ObjectNotFound: (C:\PS ping\System.Windows.Forms.dll:String) [Add-Type], ItemNotFoundException
          + FullyQualifiedErrorId: PathNotFound,Microsoft.PowerShell.Commands.AddTypeCommand
          Странно.

          Давайте так, хоть где-то удалось запустить? =)


          1. IDDQDesnik
            15.08.2019 15:45

            PS 5 хоть и ругается, но работает.

            Про динамик понятно (1 и 3 ошибки), просто по хорошему стоит сначала проверять его наличие. А вторая ошибка явный баг.

            Нерабочий PS 6 это оказывается «фича» techcommunity.microsoft.com/t5/Windows-PowerShell/Powershell-Core-loading-assemblies/m-p/355539 stackoverflow.com/questions/57212545/how-to-add-some-types-in-powershell-6-i-can-do-in-powershell-5 возможно заработает в 7.


  1. osipov_dv
    15.08.2019 14:45

    Что только не придумают чтобы ставить zabbix… (можно вписать любую систему мониторинга)
    Если много свободного времени, то лучше его потратить на настройку системы, а не ее написание.


  1. Mixaill
    15.08.2019 14:46
    +1

    $script_path = "C:\Test\PS ping"

    Есть же $PSScriptRoot


    1. AlexUl Автор
      15.08.2019 14:52

      Не не зналдумал об этом.
      Действительно, можно. Внесу в изменения.


  1. vs_starosta
    15.08.2019 17:42

    1. Вы уже рисуете точки. Что мешает вам нарисовать зеленый и красный квадратик system.drawing.graphics.fillrectangle? Тогда вам ненужны эти картинки-лампочки.
    2. Зачем вы парсите тектовый файл? Скрипт сам по себе текстовый файл. Объявите хэш таблицу с ip и описнаиями в начале скрипта и пробегайте её в цикле. Надо добавить что-то новое, убрать старое — открыли блокнотом и поправили одну переменную.


  1. HomeDimoN
    16.08.2019 09:42
    +1

    Как вставить свою иконку в форму
    Ресурс храниться внутри скрипта.
    #каринка в base64
    base64IconString = "iVB...mCC"
    $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
    $bitmap.BeginInit()
    $bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64IconString)
    $bitmap.EndInit()
    $bitmap.Freeze()
    
    $form.Icon = $bitmap
    

    почти таким же способом можно подгрузить и другие ресурсы


    1. AlexUl Автор
      16.08.2019 10:09

      Формы проще рисовать через XAML (есть графические редкаторы).
      Возможно, но я в тех туториолах что я находил для этой утилиты, была такая примитивная реализация.
      Для уведомления можно сделать не только звук но и Baloon Popup в трее.
      Любопытно, особенно то, что в спойлерах, возьму на заметку. =)


  1. turbo_dimon
    16.08.2019 10:06

    Есть уже готовый софт для таких целей PingInfoView