Содержание
Active Directory
Показать значение атрибута msDS-cloudExtensionAttribute1 текущего пользователя
# Import AD Module Import-Module ActiveDirectory get-aduser $env:UserName -Properties * | Select msDS-cloudExtensionAttribute1 # Имя компа: # $env:computername # Имя домена: # $env:DomainName
Вывести список пользователей и показать логин и msDS-cloudExtensionAttribute1
# Из OU get-aduser -Filter * -SearchBase "OU=Руководство,DC=domain,DC=local" -Properties Enabled,msDS-cloudExtensionAttribute1 | select Name,SamAccountName,msDS-cloudExtensionAttribute1 | ft -AutoSize # По группе Get-ADGroupMember "CN=Заместители директоров,OU=Groups,DC=domain,DC=local" | get-aduser -Properties Enabled,msDS-cloudExtensionAttribute1 | select Name,SamAccountName,msDS-cloudExtensionAttribute1 | ft -AutoSize
Извлечь атрибут msDS-CloudExtensionAttribute1 у пользователей "Рогов и копыт", и добавить значения в группу "Компьютеры для тестирования"
get-aduser -Filter {Company -eq "Рога и копыта"} -SearchBase "OU=Users,DC=domain,DC=local" -Properties * | Select -expand msDS-CloudExtensionAttribute1 | foreach {Add-ADGroupMember -Identity "CN=Компьютеры для тестирования,OU=Группы компьютеров,OU=Resource,DC=domain,DC=local" -Members (Get-ADComputer $_)}
Извлечь компы из выгрузки из KSC и поместить их в соответствующие группы в AD для установки новых версий Хрома
$comps = Import-Csv "C:\Users\user\Desktop\Chrome.csv" -Delimiter "`t" |select -expand "Имя компьютера" | % {Get-ADComputer $_ -properties operatingsystem} $legacy,$new = $comps.where({$_.operatingsystem -match "XP|2003"}, 'Split') "Компьютеры для установки Google Chrome (Windows XP)" |Add-ADGroupMember -Members $legacy "Компьютеры для установки Google Chrome" |Add-ADGroupMember -Members $new
Имя компьютера, время входа в AD + файл
# write to file $file = "\\server\logons\$env:USERNAME.csv" $exist = Test-Path $file $t = get-date -Format "yyyy.MM.dd;HH:mm:ss" if ($exist -ne $true) {echo "Дата;Время;Логин;Компьютер" |Out-File $file -Encoding utf8} echo "$t;$env:USERNAME;$env:COMPUTERNAME" |Out-File $file -Encoding utf8 -Append # write last logged-on comp name to msds-cloudextensionattribute10 $Searcher = New-Object DirectoryServices.DirectorySearcher $Searcher.Filter = "(&(objectCategory=person)(anr=$env:username))" $Searcher.SearchRoot = 'LDAP://DC=domain,DC=ru' $path = $Searcher.FindOne() |select -expand path $user = [ADSI]"$path" $user.Put("msds-cloudextensionattribute10", "$env:computername") # write last bootup time to msds-cloudextensionattribute11 $boottime = Get-WmiObject win32_operatingsystem $boottime = $boottime.ConverttoDateTime($boottime.lastbootuptime) $boottime = $boottime.toString("dd.MM.yyyy HH:mm:ss") $user.Put("msds-cloudextensionattribute11", "$boottime") $user.SetInfo()
На пользовательских машинах не установлены RSAT, поэтому
Set-ADUser $env:username -Add @{'msDS-cloudExtensionAttribute1'="$env:computername"}
не пройдёт.
Дополнительно - примеры запросов к AD:
#user properties $san = 'user' $getad = (([adsisearcher]"(&(objectCategory=User)(samaccountname=$san))").findall()).properties $getad #Computer properties $pc = 'computername' $getad = (([adsisearcher]"(&(objectCategory=Computer)(name=$pc))").findall()).properties $getad
https://social.technet.microsoft.com/Forums/Lync/en-US/fd0b0a1c-b6dc-4657-8a95-21b6a11377df/using-ad-module-without-loading-rsat?forum=winserverpowershell
https://social.technet.microsoft.com/wiki/contents/articles/4231.working-with-active-directory-using-powershell-adsi-adapter.aspx
https://social.technet.microsoft.com/Forums/en-US/646ffe45-5ba5-461c-895f-a557e35cbc0d/need-to-set-extensionattribute1-of-an-adgroup-through-powershell
Найти пользователя в AD по части ФИО и показать информацию
[CmdletBinding()] Param( [Parameter(Mandatory=$True)] $user ) $cusers = get-aduser -Filter "(ObjectClass -eq 'user') -AND (SamAccountName -notlike '*.crm')" -SearchBase "dc=domain,dc=ru" |? Name -like "*$user*" foreach ($cuser in $cusers) { Get-ADUser $cuser -Properties * | select @{n="ФИО"; e={$_.Name}}, ` @{n="Логин"; e={$_.sAMAccountName}}, ` @{n="Кабинет"; e={$_.physicalDeliveryOfficeName}}, ` @{n="Телефон"; e={$_.telephoneNumber}}, ` @{n="Компания"; e={$_.'msDS-cloudextensionAttribute1'}}, ` @{n="Управление"; e={$_.'msDS-cloudextensionAttribute2'}}, ` @{n="Дирекция"; e={$_.'msDS-cloudextensionAttribute3'}}, ` @{n="Отдел"; e={$_.'msDS-cloudextensionAttribute4'}}, ` @{n="Служба"; e={$_.'msDS-cloudextensionAttribute5'}}, ` @{n="Группа"; e={$_.'msDS-cloudextensionAttribute6'}}, ` @{n="Должность"; e={$_.'msDS-cloudextensionAttribute7'}}, ` @{n="Имя компьютера"; e={$_.'msDS-cloudextensionAttribute10'}}, ` @{n="Время последней загрузки"; e={$_.'msDS-cloudextensionAttribute11'}} } pause
Генератор паролей
$length = 32 [int]$quarter = $length/4 [int]$residual = $length-$quarter*2 # if special are used # [int]$residual = $length-$quarter*3 $cletters = [char[]](0..255) -clike '[A-Z]' -notmatch "I|O|B" # except I, O, B $letters = [char[]](0..255) -clike '[a-z]' -notmatch "l" # except l $numbers = [char[]](0..255) -clike '[0-9]' -notmatch "1|0|8" # except 1, 0, 8 # $special = @("!","-","#","$","%","^","&","_",".",";") # add more if you need $rcletters = Get-Random -InputObject $cletters -count $quarter $rnumbers = Get-Random -InputObject $numbers -count $quarter # $rspecial = Get-Random -InputObject $special -count $quarter $rletters = Get-Random -InputObject $letters -count $residual $password = $rcletters + $rletters + $rnumbers # $password = $rcletters + $rletters + $rnumbers + $rspecial $password = (Get-Random -InputObject $password -count $length) -join "" $password
# x = length in characters # y = minimum number of non-alphanumeric characters Add-Type -AssemblyName System.web [System.Web.Security.Membership]::GeneratePassword(x,y)
ФИО в логин
$src = Import-Excel 'C:\scripts\Список сотрудников.xlsx' -StartRow 4 -StartColumn 2 |select -expand ФИО $alldata = @() foreach ($fio in $src) { $login = $fio -split " " $f = $login[0] $i = ($login[1]).substring(0,1) $o = ($login[2]).substring(0,1) $login = $f + $i + $o # ьи, ье, ьё - Ильин, yuyu, yaya - Юлия Юрьевна $login = $login -replace 'ий','y' -replace 'ый','y' -replace 'ьи','yi' -replace 'ье','ye' -replace 'ьё','ye' ` -replace 'а','a' -replace 'б','b' -replace 'в','v' -replace 'г','g' -replace 'д','d' -replace 'е','e' ` -replace 'ё','e' -replace 'ж','zh' -replace 'з','z' -replace 'и','i' -replace 'й','y' -replace 'к','k' ` -replace 'л','l' -replace 'м','m' -replace 'н','n' -replace 'о','o' -replace 'п','p' -replace 'р','r' ` -replace 'с','s' -replace 'т','t' -replace 'у','u' -replace 'ф','f' -replace 'х','kh' -replace 'ц','ts' ` -replace 'ч','ch' -replace 'ш','sh' -replace 'щ','sch' -replace 'ь' -replace 'ы','y' -replace 'ъ' ` -replace 'э','e' -replace 'ю','yu' -replace 'я','ya' -replace 'yuyu','yy' -replace 'yaya','yy' ` -join "" $raw = [pscustomobject]@{ FIO = $fio Login = $login } $alldata += $raw } $alldata # $alldata |Export-Csv c:\temp\logins.csv -Delimiter ';' -Encoding utf8 -NoTypeInformation
Скопировать группы, содержащие "КТ " из одной учётки в другую
[CmdletBinding()] Param( [Parameter(Mandatory=$True)] $from, [Parameter(Mandatory=$True)] $to ) $CopyFromUser = Get-ADPrincipalGroupMembership $from |where {$_.name -like '*КТ *'} $CopyToUser = Get-ADPrincipalGroupMembership $to $CopyFromUser | Where{$CopyToUser -notcontains $_} | Add-ADGroupMember -Member $to
Добавить комп в группу
[CmdletBinding()] Param( [Parameter(Mandatory=$True)] $comp ) add-ADGroupMember -Identity "Компьютеры для установки КриптоПро CSP 4.0.9944" -Members (get-adcomputer $comp) # Добавить много компов "Имя группы" |Add-ADGroupMember -Members ` ("pc1722","pc1723","pc1724","pc1725","pc1726","pc1727","pc1728","pc1729","pc1730","pc1731" |Get-ADComputer)
Показать включённых юзеров с датой входа более 2 недель
get-aduser -SearchBase "OU=Административный департамент,OU=Users,domain,DC=ru" -Properties lastlogondate -Filter {enabled -eq $true} | ? Lastlogondate -lt (Get-Date).Adddays(-14) |sort lastlogondate -Descending |ft name,lastlogondate
Лог входов в систему
$file = "\\server\logon-log\$env:USERNAME.csv" $exist = Test-Path $file $t = get-date -Format "yyyy.MM.dd;HH:mm:ss" if ($exist -ne $true ) { echo "Дата;Время;Логин;Компьютер" |Out-File $file -Encoding utf8 } echo "$t;$env:USERNAME;$env:COMPUTERNAME" |Out-File $file -Encoding utf8 -Append
Запуск из GPO:
%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File \\path-to-server\scripts\logon-log.ps1
Список учёток с неправильно заполненными инициалами
Get-ADUser -filter 'name -like "*на" -or name -like "*ич"' -SearchBase "OU=1_Users,OU=test,DC=domain,DC=ru" -Properties initials | ? initials -notmatch '\w\. \w\.' |select -expand name |sort
Поставить галку "Защитить объект от случайного удаления" для всех OU домена
Get-ADOrganizationalUnit -filter * |Set-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $true
Настройка локальных машин
Отключение энергосбережения для сетевого адаптера
# Disable "Allow the computer to turn off the device to save power" foreach ($id in (gwmi win32_networkadapter |? {$_.physicaladapter -and $_.name -notmatch "virtual|wireless|microsoft" -and $_.Manufacturer -ne "Microsoft"}).deviceid) { Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\$('{0:d4}' -f [int]$id)" -Name PnPCapabilities -Value 24 } # Вариант выборки сетевого адаптера: # gwmi Win32_NetworkAdapter |? {$_.PNPDeviceID -notlike "ROOT\*" -and $_.Manufacturer -ne "Microsoft" -and $_.ConfigManagerErrorCode -eq 0}
https://support.microsoft.com/en-us/help/2740020/information-about-power-management-setting-on-a-network-adapter
https://techcommunity.microsoft.com/t5/exchange-team-blog/do-you-have-a-sleepy-nic/ba-p/590996
https://gallery.technet.microsoft.com/scriptcenter/Disable-turn-off-this-f74e9e4a
Информация по TCP-соединениям
$comps = "PC1708","PC1163" foreach ($comp in $comps) { Invoke-Command -ComputerName $comp -ScriptBlock { netstat -vb } |Out-File c:\temp\$comp.txt } foreach ($comp in $comps) { "--- $comp ---" (gc c:\temp\$comp.txt) -match '\[.*\]' |group }
--- PC1708 --- Count Name Group ----- ---- ----- 33 [svchost.exe] { [svchost.exe], [svchost.exe], [svchost.exe], [svchost.exe]...} 1 [1cv8c.exe] { [1cv8c.exe]} 492 [Iwproxy.exe] { [Iwproxy.exe], [Iwproxy.exe], [Iwproxy.exe], [Iwproxy.exe]...} 1 [1cv8.exe] { [1cv8.exe]} 5 [OUTLOOK.EXE] { [OUTLOOK.EXE], [OUTLOOK.EXE], [OUTLOOK.EXE], [OUTLOOK.EXE]...} 244 [chrome.exe] { [chrome.exe], [chrome.exe], [chrome.exe], [chrome.exe]...} 4 [IWDeployAgent.exe] { [IWDeployAgent.exe], [IWDeployAgent.exe], [IWDeployAgent.exe], [IWDeployAgent.exe]} 6 [iwdmc.exe] { [iwdmc.exe], [iwdmc.exe], [iwdmc.exe], [iwdmc.exe]...} 2 [iwshcs.exe] { [iwshcs.exe], [iwshcs.exe]} 2 [LMS.exe] { [LMS.exe], [LMS.exe]} 2 [DM.Client.exe] { [DM.Client.exe], [DM.Client.exe]} --- PC1163 --- 456 [Iwproxy.exe] { [Iwproxy.exe], [Iwproxy.exe], [Iwproxy.exe], [Iwproxy.exe]...} 3 [OUTLOOK.EXE] { [OUTLOOK.EXE], [OUTLOOK.EXE], [OUTLOOK.EXE]} 1 [spoolsv.exe] { [spoolsv.exe]} 1 [1cv8c.exe] { [1cv8c.exe]} 1 [1cv8.exe] { [1cv8.exe]} 228 [chrome.exe] { [chrome.exe], [chrome.exe], [chrome.exe], [chrome.exe]...} 4 [IWDeployAgent.exe] { [IWDeployAgent.exe], [IWDeployAgent.exe], [IWDeployAgent.exe], [IWDeployAgent.exe]} 6 [iwdmc.exe] { [iwdmc.exe], [iwdmc.exe], [iwdmc.exe], [iwdmc.exe]...} 2 [iwshcs.exe] { [iwshcs.exe], [iwshcs.exe]} 2 [DM.Client.exe] { [DM.Client.exe], [DM.Client.exe]}
Таблички, отчёты, массивы
+=
удаляет старый и создаёт новый массив, т. к. он фиксированного размера, и при большом кол-ве элементов работает очень медленно.
Лучше использовать [System.Collections.ArrayList]
или [System.Collections.Generic.List[]]
https://batishchev.ru/blog/all/arraylist-i-proizvoditelnost-massivov-v-powershell/
https://adamtheautomator.com/powershell-array/
$arrayList = [System.Collections.ArrayList]::new() $arrayList.add("aaa") > $null
[System.Collections.Generic.List[]]
- универсальный список с возможностью указания типа массива. Что удобно - в отличие от ArrayList метод Add не возвращает значение, поэтому для него не нужно выполнять void.
$a = [System.Collections.Generic.List[int]]::new() # числовой массив $a = [System.Collections.Generic.List[string]]::new() # строковый массив $a = [System.Collections.Generic.List[object]]::new() # любой тип объектов
Добавить строку в отчёт с двумя столбцами
$report += New-Object -TypeName psobject -Property @{folder="Всего каталогов"; sizeMB="$($report.folder.count)"}
Цвет затенения для строк
Для использования в html для фонового цвета перемежающихся строк
- #EDEDED - очень светлый серый.
- #D9D9D9 - серый чуть потемнее.
- #DBDBDB - серый. У Экселя есть шаблон, где он перемежается с #EDEDED.
- #A5A5A5 - серый темнее. Лучше пользоваться уже белым шрифтом.
- #7B7B7B - тёмно-серый. У Экселя есть шаблон, где он перемежается с #A5A5A5.
Массив русского алфавита с индексом
Почему не [char[]](0..65535) -clike '[а-я]
' - там не будет буквы ё. Поэтому
$alfabetRus = @("а","б","в","г","д","е","ё","ж","з","и","й","к","л","м","н","о", "п","р","с","т","у","ф","х","ц","ч","ш","щ","ъ","ы","ь","э","ю","я") $alfabetRus |select @{n="letter";e={$_}},@{n="index";e={$alfabetRus.indexof($_)+1}} letter index ------ ----- а 1 б 2 в 3 ...
Можно вычислить индексы символов и потом вставить их в массив для выбора
$allchars = [char[]](0..65535) ($allchars |select @{n="letter";e={$_}},@{n="index";e={$allchars.indexof($_)}}) -cmatch '[а-яё]' # А-Я 1140..1071 # а-я 1072..1103 # Ё,ё 1025,1105 # A-Z 65..90 # a-z 97..122 $alfabetRus = [char[]](1072..1077+1105+1078..1103) $alfabetRus |select @{n="letter";e={$_}},@{n="index";e={$alfabetRus.indexof($_)+1}}
Разбить массив на части определённого размера
$list = 1..1018 $chunkSize = 100 $c = [pscustomobject]@{Value = 0} $groups = $list |group {[math]::Floor($c.Value++ / $chunkSize)} $groups[0].group
Файловые операции
Удалять из папки все файлы старше текущего месяца, кроме файлов от 1, 15 и последнего дня месяца
$path="\\server\backups\folder" $files=get-childitem $path $currentmonth=(get-date).Month foreach ($file in $files) { if (($file.lastwritetime).Month -ne $currentmonth -and ($file.lastwritetime).day -ne 15 -and ($file.lastwritetime).day -ne 1 -and ($file.lastwritetime).Month -eq (($file.lastwritetime).adddays(1)).Month) { Remove-Item $path\$file -Force -Recurse } }
Удалить все файлы, кроме последних 10
gci | sort CreationTime | select -skip 10 | Remove-item -Force
Пакетное переименование файлов
Дано:
Pet Shop Boys - Happiness-47208322.mp3
Pet Shop Boys - The Pop Kids-47208325.mp3
[int]$n = 1 $files = dir 'D:\music\Pet Shop Boys - Super (2016)' -File |sort creationtime foreach($file in $files) { $a = $n.ToString("00") Rename-Item $file -NewName ($file.name -replace "^","$a ") $n = $n+1 } dir |Rename-Item -NewName {$_.name -replace "-\d+"} # сделать плейлист с именем как у текущего каталога $plsname = (Get-ItemProperty .).name (dir -file *.mp3).name |out-file .\"$plsname.m3u" -Encoding UTF8
Результат:
01 Pet Shop Boys - Happiness.mp3
02 Pet Shop Boys - The Pop Kids.mp3
Дано:
1 - Introduction.mp4
10 - Lesson 1 Lab.mp4
100 - Understanding Why Decoupling is Important.mp4
$files = dir 'D:\Видео\Video Collection' foreach ($file in $files) { $num,$rest = $file.name -split ' - ',2 Rename-Item $file.FullName -NewName "$(([int]$num).ToString("000")) - $rest" #-WhatIf }
Результат:
001 - Introduction.mp4
010 - Lesson 1 Lab.mp4
100 - Understanding Why Decoupling is Important.mp4
Сортировка
Если отсортировать файлы вида file1.txt - file100.txt, то выйдет
file1.txt file10.txt file11.txt ... file18.txt file19.txt file2.txt file20.txt file21.txt и т. д.
Чтобы отсортировать по-человечески:
dir "C:\temp\folder\*.txt" |sort {[int]($_.basename -replace '\D')}
Посмотреть квоты на файловом сервере
Get-FSRMQuota |? path -Match 'отдел' |select @{n='Путь';e={$_.path}},@{n='Использовано (ГБ)';e={$_.usage / 1GB -as [int]}},@{n='Всего (ГБ)';e={$_.size / 1GB -as [int]}}
Ожидание снятия блокировки файла и копирование
function Test-FileLock { param ( [parameter(Mandatory=$true)][string]$Path ) $oFile = New-Object System.IO.FileInfo $Path if ((Test-Path -Path $Path) -eq $false) { return $false } try { $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) if ($oStream) { $oStream.Close() } return $false } catch { return $true # file is locked by a process } } $from = '\\example.com\share\users\Mihailov-20230320.pst' $to = '\\pc01\c$\Users\Soloviev\Documents\Файлы Outlook' do { sleep 30 } until (-not (Test-FileLock $from)) Write-Host -fore green "Copying..." cp $from $to # Test-Path "$to\$((Get-Item $from).name)"
https://stackoverflow.com/questions/24992681/powershell-check-if-a-file-is-locked
Создание тестовых файлов с датами изменения и создания в прошлом
$t = Get-Date $folder = "D:\temp\from" 1..100 |% { $fileTime = $t.AddDays(-$_) $fileBaseName = $fileTime.ToString("yyyy.MM.dd") $filePath = "$folder\$fileBaseName.txt" $file = New-Item $filePath -ItemType File Set-Content $filePath -Value $fileBaseName $file.CreationTime = $fileTime $file.LastWriteTime = $fileTime }
Удалить файл из корзины
$file = 'C:\temp\out — копия (3).txt' (New-Object -ComObject Shell.Application).NameSpace(0x0a).Items() |? { ($_.ExtendedProperty("{9B174B33-40FF-11D2-A27E-00C04FC30871} 2") + '\' + $_.name + '.' + ($_.path -replace '.*\.')) -eq $file } |del
Работа с текстом
Программа для создания большого файла txt: Dummy File Creator.
Конвертировать строку во время
$folder = "D:\temp\20211025" $reportDate = [datetime]::parseexact($folder.Split("\")[-1], 'yyyyMMdd', $null) 25 октября 2021 г. 0:00:00
https://stackoverflow.com/questions/38717490/convert-a-string-to-datetime-in-powershell
Убрать из файла все строки, содержащие плюс
(Get-Content "C:\temp\test.txt") -notmatch '\+' |Set-Content "C:\temp\test-out.txt"
http://stackoverflow.com/questions/39828780/delete-lines-containing-specific-character-in-text-file
Каждое слово с большой буквы
(Get-Culture).TextInfo.ToTitleCase('текст, каждое слово которого будет начинаться с большой буквы')
Фильтр отчётов Cobian Backup
cd D:\Downloads Expand-Archive .\Журналы.zip cd .\Журналы gc *.txt |Select-String ERR |out-file -width 30000 .\errors.txt Invoke-Item .\errors.txt
Выбрать каждую N строку в файле
# каждую 1, 5, 9, 13, 17 и т. д. gc "D:\temp\test.txt" |? {($_.ReadCount % 4) -eq 1}
Замена ошибок в тексте (afterscan)
-replace '([\.\,\?\!])(\w)','$1 $2' ` # Добавить пробел между знаками препинания и следующим словом -replace '\s+([\.\,\?\!])','$1' ` # Убрать пробел между словом и знаком препинания -replace "\s+$" ` # Убрать пробелы в конце строки -replace "…","..." ` # Заменить символ троеточия на реальные три точки -replace '(\d)(\D)','$1 $2' ` # Добавить пробел между числом и следующим за ним словом -replace "\.$",".`n" # Добавить пустую строку, если строка заканчивается точкой -replace '(?<=\d)(\s+)?([\.\:])(\s+)?(\d+)','$2$4' # Убрать пробелы в датах (где между цифрами . и :)
Обработка больших файлов
Надо применять методы .NET и упаковывать regex, т. к. get-content
очень медленный.
function ExtractTo-Chunk ($in,$out,$regex) { [regex]$regex = $regex [System.IO.File]::AppendAllText("$out", ([System.IO.File]::ReadAllLines("$in") -match $regex -join [Environment]::NewLine)) } ExtractTo-Chunk -in "C:\temp\big.log" -out "C:\temp\chunk1.log" -regex "192.168.1.10" ExtractTo-Chunk -in "C:\temp\big.log" -out "C:\temp\chunk1.log" -regex "10.0.0.8"
Reading large text files with Powershell
Speed issue on -match operation for large text file
PowerShell scripting performance considerations
Быстрое чтение файла в массив:
[io.file]::readalllines("C:\temp\file.txt") [io.file]::readalllines("C:\temp\file.txt",[System.Text.Encoding]::UTF8) [io.file]::readalllines("C:\temp\file.txt",[System.Text.Encoding]::GetEncoding('windows-1251'))
Склонение слов в предложении с числительным
function Get-Declension ($pre1,$n,$w1,$w2,$w5) { switch -Regex ($n) { '(?<!1)1$' {$p = $pre1 -replace ''; $w = $w1} '(?<!1)[234]$' {$p = $pre1 -replace '(а|(?<![ао]))$','о'; $w = $w2} default {$p = $pre1 -replace '(а|(?<![ао]))$','о'; $w = $w5} } if ($pre1) {"$p $n $w"} else {"$n $w"} } "Машина проехала $(Get-Declension -n 2 -w1 километр -w2 километра -w5 километров)" "Одобрено $(Get-Declension -n 101 -w1 обновление -w2 обновления -w5 обновлений)" "Всего $(Get-Declension -n 22 -w1 "активный пользователь" -w2 "активных пользователя" -w5 "активных пользователей")" "$(Get-Declension -pre1 Удален -n 221 -w1 "каталог" -w2 "каталога" -w5 "каталогов")" "$(Get-Declension -pre1 Удалена -n 11 -w1 "папка" -w2 "папки" -w5 "папок")" "$(Get-Declension -pre1 Посажен -n 2511 -w1 "дуб" -w2 "дуба" -w5 "дубов")" "$(Get-Declension -pre1 Разбито -n 4 -w1 "яйцо" -w2 "яйца" -w5 "яиц")" Машина проехала 2 километра Одобрено 101 обновление Всего 22 активных пользователя Удален 221 каталог Удалено 11 папок Посажено 2511 дубов Разбито 4 яйца
Список модулей PHP из буфера по алфавиту и с переносом строки
$list = (Get-Clipboard) -replace '\s+\\$' |sort $total = $list.count $c = 1 $list |% { if ($c -eq $total) {$_} else {$_ + " \"} $c++ } |Set-Clipboard
Исходный список
php81-fpm \ php81-phar \ php81-iconv \ php81-mbstring \ php81-openssl \ php81-curl \ php81-dom \ php81-pdo_mysql \ php81-exif \ php81-pcntl \ php81-bcmath \ php81-ctype \ php81-gd \ php81-soap \ php81-zip \ php81-sockets \ php81-session \ php81-fileinfo \ php81-tokenizer \ php81-simplexml \ php81-xml \ php81-xmlreader \ php81-xmlwriter php81-redis
Результат
php81-bcmath \ php81-ctype \ php81-curl \ php81-dom \ php81-exif \ php81-fileinfo \ php81-fpm \ php81-gd \ php81-iconv \ php81-mbstring \ php81-openssl \ php81-pcntl \ php81-pdo_mysql \ php81-phar \ php81-redis \ php81-session \ php81-simplexml \ php81-soap \ php81-sockets \ php81-tokenizer \ php81-xml \ php81-xmlreader \ php81-xmlwriter \ php81-zip
Замена многострочного блока
Управление групповой политикой body { font-size:68%;font-family:MS Shell Dlg; margin:0px,0px,0px,0px; border: 1px solid #666666; background:#F6F6F6; width:100%; ... overflow:visible; } button { display:none; } .head { color:#000000; background:#FFFFFF; border:1px solid #000000; } } Путь размещения параметров: Объяснение
(gc 'D:\Downloads\file.txt' -Raw) -replace 'body((.|\n)*)\} \}'
Замена нескольких пустых строк на одну
(gc 'D:\Downloads\file.txt' -Raw) -replace '(\r?\n){2,}', '$1$1'
Преобразование строк в таблицу
Opera Hotlist version 2.0 Options: encoding = utf8, version=3 #CONTACT ID=11 NAME=Justynka CREATED=1195505237 MAIL=JUSTYNA66@gmail.com ICON=Contact0 #CONTACT ID=12 NAME=Leszek CREATED=1195677687 MAIL=Leszek@domena.pl ICON=Contact0 #CONTACT ID=13 NAME=Iwona Kwiatkowska CREATED=1196277590 MAIL=iwon.kwiat@op.pl ICON=Contact0 #FOLDER ID=15 NAME=Kosz CREATED=1195505227 TRASH FOLDER=YES UNIQUEID=EAF22324295C86499476802CC76DE41E - #CONTACT ID=16 NAME=Ania CREATED=1195505237 MAIL=Ania.Nowak@poczta.com ICON=Contact0
Вычистить ненужные строки и мусор, разделить текст на сегменты, создать структурированные объекты. Разделителем для ConvertFrom-StringData
должен быть =
.
(Get-Content .\opera.adr -Raw) -replace '^Opera[\s\S]*?#CONTACT' -split '#CONTACT|#FOLDER' |% { $props = ConvertFrom-StringData -StringData ($_ -replace '\n-\s+') New-Object PSOBject -Property $props |select Name, Mail }
https://powershellmagazine.com/2014/09/09/using-the-convertfrom-string-cmdlet-to-parse-structured-text/
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-stringdata?view=powershell-5.1
Графика
Диаграммы
На примере ежечасно снимаемых логов вошедших по VPN пользователей.
Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Windows.Forms.DataVisualization $Chart = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Chart $Chart.Size = '800,400' $ChartArea = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.ChartArea $ChartArea.AxisX.Title = 'Часы' $ChartArea.AxisY.Title = 'Кол-во пользователей' $ChartArea.AxisX.Interval = '1' $ChartArea.AxisX.LabelStyle.Enabled = $true $ChartArea.AxisX.LabelStyle.Angle = 90 $ChartArea.AxisX.MajorGrid.Enabled = $false # No grid $ChartArea.AxisY.MajorGrid.Enabled = $false # No grid $Chart.ChartAreas.Add($ChartArea) $Chart.Series.Add('VPNUsers') > $null $Chart.Series['VPNUsers'].IsValueShownAsLabel = $true # Show values on columns' tops # Chart type - Column $Chart.Series['VPNUsers'].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Column # Looping files $txtFiles |% { $hour = $_.BaseName -replace ".*-" -replace "\d{4}$" $usersCount = (gc $_.FullName |% {($_ -split "'")[1]} |sort -Unique).count $Value = $Chart.Series['VPNUsers'].Points.AddXY("$hour","$usersCount") } # Title $Title = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Title $Chart.Titles.Add($Title) $Chart.Titles[0].Text = 'Статистика по часам' # Saving PNG file $png = "$env:temp\VPNUsersChart.png" $Chart.SaveImage("$png", "PNG")
https://www.powershellbros.com/building-first-chart-report-powershell/
https://learn-powershell.net/2016/09/18/building-a-chart-using-powershell-and-chart-controls/
https://stackoverflow.com/questions/6413182/how-to-display-values-in-asp-net-chart-control
https://stackoverflow.com/questions/1058572/hiding-the-gridlines-on-an-asp-net-chart-control/1368853
Интернет
Скачать файлы по списку
$url = "https://reasonstation.nl/downloads/yamaha/mp3-midi/" $pattern = ".MID" (curl $url).links |select -expand href |Select-String -SimpleMatch "$pattern" | % { $f = $url + $_ curl $f -OutFile "$env:userprofile\Downloads\$_" }
Отправить письмо
Без запроса пароля.
# Сохранение шифрованного пароля в файл Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File C:\temp\securestring.txt $pass = cat C:\temp\securestring.txt | convertto-securestring $mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist "username",$pass # С Яндекса Send-MailMessage -SmtpServer smtp.yandex.ru -UseSsl -Encoding utf8 -From "username@yandex.ru" -Subject "Test message" -Body "Body of test message" -To "username@domain.com" -Credential $mycred
Игнорировать самоподписанный сертификат SMTP
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { return $true } Send-MailMessage -SmtpServer "mail.example.com" -from "script@example.com" -to "user@example.com" -Subject "Hello" -body "<H1>Hello</H1>" -BodyAsHtml -Encoding UTF8 -UseSsl
Письмо с картинкой в тексте
Send-MailMessage -To "testovtt@domain.ru" -From "birthday@domain.ru" ` -SmtpServer "mail.domain.ru" -Subject "С днём рожденья!" ` -Body " <h3>Уважаемый Тест Тестович!</h3> <p>Поздравляю вас с днём рождения!</p> <img src='cid:birthday.jpg'> <p><i>С уважением, скрипт.</i></p> " -Attachments "C:\scripts\birthday.jpg" -BodyAsHtml -Encoding utf8
https://stackoverflow.com/questions/52837468/embed-images-in-email-and-send-via-powershell
Сколько дней осталось до окончания действия SSL-сертификата
С помощью curl (надо скачивать с сайта, встроенный в винду многого не умеет)
$urls = @( "google.com" "mail.ru" "yandex.ru" "kubernetes.io" ) #$urls = gc "C:\temp\sites.txt" |sort $t = get-date function Decode-UnicodeEscaped { param([string]$str) # decode if unicode escaped strings # https://stackoverflow.com/questions/18450218/unescape-unicode-string-powershell [Regex]::Replace($str, "\\[Uu]([0-9A-Fa-f]{4})", {[char]::ToString([Convert]::ToInt32($args[0].Groups[1].Value, 16))} ) } $report = @() $urls |% { $dump = & C:\scripts\curl\curl.exe "https://$_" -Iv --stderr - $issuer = $dump -match "issuer:" -replace ".+: " $expdate = $dump -match "expire date:" -replace ".+: " -replace "\s+"," " $expdate = Get-Date $("{0} {3} {1} {2} {4}" -f "$expdate".Split()) $obj = [pscustomobject]@{ Сайт = $_ 'Конец срока действия' = $expdate 'Осталось дней' = if ($expdate) {($expdate - $t).days}; 'УЦ' = Decode-UnicodeEscaped $issuer } $report += $obj Remove-Variable dump,expdate,issuer } $report |sort 'Конец срока действия'
https://nickjanetakis.com/blog/using-curl-to-check-an-ssl-certificate-expiration-date-and-details
Рабочие методы в чистом Powershell: https://habr.com/ru/company/t1_cloud/blog/661107/
Ранее работавший метод: https://stackoverflow.com/a/20014855
Получить HTML-объекты определённого класса
$url = "https://jut.su/naruuto/season-2/episode-301.html" # Нужно указать первый элемент [0], даже если он единственный. (iwr $url).ParsedHtml.body.getElementsByClassName('header_video anime_padding_for_title_post_naruto')[0].innertext
Информация
Вывод системной информации:
# Computer name $comp = $env:COMPUTERNAME # login $login = $env:username # Full user name $fullusername = ([adsi]"WinNT://$env:userdomain/$env:username,user").fullname # IP address $ip = Get-NetIPAddress -AddressFamily IPv4 | where {$_.interfacealias -NotLike '*Loopback*' -AND $_.interfacealias -NotLike '*virtual*'} | select ipaddress -ExpandProperty ipaddress # MAC $mac = Get-NetAdapter -Physical | select macaddress -ExpandProperty macaddress # IP и MAC для PoSH старых версий # Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled = True" | where {$_.description -notlike '*virtual*'} |select IPAddress,MACAddress |fl @{n="IP"; e={$_.IPAddress}},@{n="MAC"; e={$_.MACAddress}} # Time mark $mark = get-date -UFormat "%d.%m.%Y %H:%M" echo "Имя компьютера: $comp" echo "Логин пользователя: $login" echo "ФИО пользователя: $fullusername" echo "IP: $ip" echo "MAC: $mac" echo "Время подготовки отчёта: $mark"
Список установленных программ
# Узнать удалённо, что стоит на server1 Invoke-Command -cn server1 -ScriptBlock {Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | select DisplayName,DisplayVersion,Publisher,InstallDate,UninstallString} # Для 32-битных приложений на 64-битной ОС: # HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* # Вариант через Get-WmiObject (медленно) gwmi win32_product -ComputerName server1 -Filter "Name like 'Microsoft Office%'"
https://devblogs.microsoft.com/scripting/use-powershell-to-find-installed-software/
https://social.technet.microsoft.com/Forums/en-US/dc42dd1d-737e-4f64-8fe2-e421f655836f/powershell-script-to-identify-office-version-on-a-remote-system-using-computer-name?forum=ITCG
Универсальный вариант: https://serverfault.com/questions/1111419/how-to-get-a-complete-list-of-all-installed-software-via-powershell
Создание окон
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") [System.Windows.Forms.MessageBox]::Show("Message Text","Title",1,48)
Первое число:
0 OK
1 OKCancel
2 AbortRetryIgnore
3 YesNoCancel
4 YesNo
5 RetryCancel
Второе число:
0 None
16 Hand
16 Error
16 Stop
32 Question
48 Exclamation
48 Warning
64 Asterisk
64 Information
https://michlstechblog.info/blog/powershell-show-a-messagebox/
Температура процессора
((gwmi -Namespace root\wmi MSAcpi_ThermalZoneTemperature).CurrentTemperature |sort |select -last 1) / 100
CMD:
@echo off for /f "skip=1 tokens=2 delims==" %%A in ('wmic /namespace:\\root\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature /value') do set /a "HunDegCel=(%%~A*10)-27315" echo %HunDegCel:~0,-2%.%HunDegCel:~-2% Degrees Celsius pause
Аптайм
read-host "Имя компа" |Set-Variable n gcim win32_operatingsystem -computername $n | select Caption,Version,@{Name="Uptime";Expression={(Get-Date) - $_.lastBootUpTime}},PScomputername
Даты установки Windows
$report = gci -Path HKLM:\System\Setup\Source* |% { Get-ItemProperty -Path Registry::$_} | select ProductName, ReleaseID, CurrentBuild, @{n="InstallDate"; e={([DateTime]'1/1/1970').AddSeconds($_.InstallDate)}} | sort {[int]($_.CurrentBuild)} $report += gcim Win32_OperatingSystem |select @{n='ProductName';e={($_.Caption -replace 'майкрософт').Trim()}},ReleaseID,@{n='CurrentBuild';e={$_.BuildNumber}},InstallDate $report ProductName ReleaseID CurrentBuild InstallDate ----------- --------- ------------ ----------- Windows 10 Pro 10240 08.02.2016 7:39:56 Windows 10 Pro 1511 10586 08.02.2016 8:51:37 Windows 10 Pro 1607 14393 04.09.2016 14:17:50 Windows 10 Pro 1703 15063 10.07.2017 7:48:10 Windows 10 Pro 1709 16299 18.10.2017 19:55:47 Windows 10 Pro 1803 17134 04.05.2018 3:53:16 Windows 10 Pro 1809 17763 23.12.2018 20:11:00 Windows 10 Pro 1909 18363 06.12.2019 18:23:14 Windows 10 Pro 2009 19044 06.06.2020 6:43:13 Windows 11 Pro 22000 23.02.2022 12:42:49
Состояние диска
Добавить проверку и отправку писем, если статус не ОК
$i = gcim Win32_DiskDrive |? Partitions -gt 0 | select SystemName,Model,@{n='Size(GB)';e={$_.Size / 1GB -as [int]}},InterfaceType,Status,MediaType,SerialNumber if ($i.Status -ne "OK") {Write-Host "Что-то с жёстким диском!";$i}
Total LBAs Written в мегабайты/гигабайты/терабайты
[single]$lbaWritten = Read-Host "Введите кол-во записанных LBA" [single]$lbaSize = Read-Host "Введите размер блока, если он отличается от стандартного 512 КБ" if (-not $lbaSize) {$lbaSize = 512} $bytes = $lbaWritten * $lbaSize Write-Host -fore Yellow "`nЗаписано на диск:" "$($bytes / 1MB -as [int]) МБ" "$($bytes / 1GB -as [int]) ГБ" "$(($bytes / 1TB).tostring("0.00")) ТБ"
Сокращение десятичных знаков, округление
@{n='Size(MB)';e={($_.Length/1MB).ToString("#.##")}} # строка [math]::round((0.4265867),2) # число 0,43
Внешний IP
curl https://2ip.ru
Поиск свободного IP в сети
1..254 |% { $ip = "10.1.0.$_" if (-not (Test-Connection $ip -Count 1 -Quiet)) { $ip } }
Потребление памяти несколькими одноимёнными процессами
get-process firefox |group name |select name,@{n='Memory(MB)';e={(($_.group.workingset |measure -sum).sum / 1mb).ToString('#.##')}}
Пути каталогов в профиле и системе
# Перечислить [enum]::getnames([environment+specialfolder]) AdminTools ApplicationData CDBurning CommonAdminTools CommonApplicationData CommonDesktopDirectory CommonDocuments CommonMusic ... # Вывести путь [environment]::getfolderpath('startup') C:\Users\username\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
Реестр
$regPath = "HKCU:\Software\Microsoft\Office\16.0\Outlook\Options\Mail" New-ItemProperty -Path $regPath -Name JunkMailImportLists -PropertyType Dword -Value 1 -Force > $null
Список значений -PropertyType
.
Binary REG_BINARY DWord REG_DWORD ExpandString REG_EXPAND_SZ MultiString REG_MULTI_SZ None No data type QWord REG_QWORD String REG_SZ
https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.registryvaluekind
Прочее
Таймер
$timer = [system.diagnostics.stopwatch]::StartNew() [math]::round(($timer.Elapsed.TotalSeconds),2) $timer.IsRunning True $timer.Stop()
https://www.pluralsight.com/blog/tutorials/measure-powershell-scripts-speed
Секунды в dd.hh:mm:ss
[timespan]::fromseconds("336621").tostring() 3.21:30:21
Текст в речь
$currentdate = (get-date).ToLongDateString() $currenttime = (get-date).ToLongTimeString() Add-Type -AssemblyName System.speech $speak = New-Object System.Speech.Synthesis.SpeechSynthesizer $speak.Speak("Сегодня $currentdate, текущее время $currenttime")
https://learn-powershell.net/2013/12/04/give-powershell-a-voice-using-the-speechsynthesizer-class/
Скриншот
[void][reflection.assembly]::loadwithpartialname("system.windows.forms") [system.windows.forms.sendkeys]::sendwait('{PRTSC}') Get-Clipboard -Format Image | ForEach-Object -MemberName Save -ArgumentList "c:\temp\screenshot.png"
https://www.reddit.com/r/PowerShell/comments/9y9689/take_a_screenshot_with_powershell/
Установить/обновить Powershell 7
Windows:
iex "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI"
Linux:
wget https://aka.ms/install-powershell.sh; sudo bash install-powershell.sh; rm install-powershell.sh
https://www.thomasmaurer.ch/2019/07/how-to-install-and-update-powershell-7/
Запуск скрипта Powershell из-под PSExec
@echo off title Install Powershell 5.1 set /P COMP=computername: psexec.exe \\%COMP% /s /h cmd /c powershell -executionpolicy bypass -file "\\domain.ru\share\Powershell\Win7-install-PS51.ps1"
Обработка объектов, перетащенных на ярлык
# Ярлык powershell -f "C:\temp\drag-drop.ps1" # вариант powershell -noexit -noprofile -f "C:\temp\drag-drop.ps1"
При перетаскивании на ярлык в $args
попадают полные пути объектов.
# Получить список файлов $inputFiles = @() $args |% { if ((get-item "$_").mode -match "^d") { $inputFiles += dir "$_" -File -Recurse } else { $inputFiles += get-item "$_" } }
Out-Null
Никогда не нужно пользоваться командлетом Out-Null, он тормозной. Вместо него (по убыванию быстродействия)
$array = New-Object -TypeName System.Collections.ArrayList # $null (assigned to $null) - RECOMMENDED $null = $array.Add('A') # [void] (cast to void) - RECOMMENDED [void]$array.Add('B') # $null (redirected to $null) $array.Add('C') > $null
https://powershell-guru.com/powershell-best-practice-12-avoid-out-null/
Путь к файлу процесса на удалённой машине
Get-Process
не умеет получать путь с удалённой машины. Нужно через WMI:
Get-WmiObject -Class win32_process -ComputerName $MyServer -Filter 'name like "%iexplore%"' | select processname,path
https://stackoverflow.com/questions/49348230/finding-path-of-process-on-remote-machine