scripts:ps
Различия
Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
scripts:ps [15.03.2025 07:01] – [Создание окон] viacheslav | scripts:ps [23.04.2025 10:51] (текущий) – [Псевдонимы (алиасы)] viacheslav | ||
---|---|---|---|
Строка 1: | Строка 1: | ||
+ | ===== Настройка ===== | ||
+ | ==== Псевдонимы (алиасы) ==== | ||
+ | На примере клиента Openshift. | ||
+ | <code powershell> | ||
+ | # Создать файл профиля, | ||
+ | if (!(Test-Path $profile)) {New-Item $profile -Force} | ||
+ | start $profile | ||
+ | # Вставить текст. Алиас - только сам .exe, остальное - функции. | ||
+ | New-Alias oc ' | ||
+ | function logintest { | ||
+ | $cred = Get-Credential -UserName $env: | ||
+ | oc login https:// | ||
+ | Remove-Variable cred | ||
+ | } | ||
+ | function ogp {oc get po} | ||
+ | function ol {oc logs} | ||
+ | function olf {oc logs -f} | ||
+ | </ | ||
+ | :!: Файл профиля свой у каждого приложения - Powershell, Powershell ISE, VSCode и т. п., поэтому создавать алиасы нужно для всех. | ||
+ | <code powershell> | ||
+ | # Отредактировать алиасы | ||
+ | start $profile | ||
+ | # Перечитать алиасы без закрытия текущей сессии | ||
+ | & $profile | ||
+ | </ | ||
+ | https:// | ||
+ | ===== Active Directory ===== | ||
+ | ==== Показать значение атрибута msDS-cloudExtensionAttribute1 текущего пользователя ==== | ||
+ | <code powershell> | ||
+ | # Import AD Module | ||
+ | Import-Module ActiveDirectory | ||
+ | get-aduser $env: | ||
+ | # Имя компа: | ||
+ | # $env: | ||
+ | # Имя домена: | ||
+ | # $env: | ||
+ | </ | ||
+ | |||
+ | ==== Вывести список пользователей и показать логин и msDS-cloudExtensionAttribute1 ==== | ||
+ | <code powershell> | ||
+ | # Из OU | ||
+ | get-aduser -Filter * -SearchBase " | ||
+ | # По группе | ||
+ | Get-ADGroupMember " | ||
+ | </ | ||
+ | ==== Извлечь атрибут msDS-CloudExtensionAttribute1 у пользователей " | ||
+ | <code powershell> | ||
+ | get-aduser -Filter {Company -eq " | ||
+ | </ | ||
+ | |||
+ | ==== Извлечь компы из выгрузки из KSC и поместить их в соответствующие группы в AD для установки новых версий Хрома ==== | ||
+ | <code powershell> | ||
+ | $comps = Import-Csv " | ||
+ | % {Get-ADComputer $_ -properties operatingsystem} | ||
+ | |||
+ | $legacy, | ||
+ | " | ||
+ | " | ||
+ | </ | ||
+ | ==== Имя компьютера, | ||
+ | <code powershell> | ||
+ | # write to file | ||
+ | $file = " | ||
+ | $exist = Test-Path $file | ||
+ | $t = get-date -Format " | ||
+ | if ($exist -ne $true) {echo " | ||
+ | echo " | ||
+ | |||
+ | # write last logged-on comp name to msds-cloudextensionattribute10 | ||
+ | $Searcher = New-Object DirectoryServices.DirectorySearcher | ||
+ | $Searcher.Filter = " | ||
+ | $Searcher.SearchRoot = ' | ||
+ | $path = $Searcher.FindOne() |select -expand path | ||
+ | $user = [ADSI]" | ||
+ | $user.Put(" | ||
+ | # write last bootup time to msds-cloudextensionattribute11 | ||
+ | $boottime = Get-WmiObject win32_operatingsystem | ||
+ | $boottime = $boottime.ConverttoDateTime($boottime.lastbootuptime) | ||
+ | $boottime = $boottime.toString(" | ||
+ | $user.Put(" | ||
+ | $user.SetInfo() | ||
+ | </ | ||
+ | На пользовательских машинах не установлены RSAT, поэтому | ||
+ | <code powershell> | ||
+ | Set-ADUser $env: | ||
+ | </ | ||
+ | не пройдёт. | ||
+ | |||
+ | Дополнительно - примеры запросов к AD: | ||
+ | <code powershell> | ||
+ | #user properties | ||
+ | $san = ' | ||
+ | $getad = (([adsisearcher]" | ||
+ | $getad | ||
+ | |||
+ | #Computer properties | ||
+ | $pc = ' | ||
+ | $getad = (([adsisearcher]" | ||
+ | $getad | ||
+ | </ | ||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ==== Найти пользователя в AD по части ФИО и показать информацию ==== | ||
+ | <code powershell> | ||
+ | [CmdletBinding()] | ||
+ | Param( | ||
+ | [Parameter(Mandatory=$True)] | ||
+ | $user | ||
+ | ) | ||
+ | |||
+ | $cusers = get-aduser -Filter " | ||
+ | foreach ($cuser in $cusers) { | ||
+ | Get-ADUser $cuser -Properties * | | ||
+ | select @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | @{n=" | ||
+ | } | ||
+ | pause | ||
+ | </ | ||
+ | |||
+ | ==== Генератор паролей ==== | ||
+ | <code powershell> | ||
+ | $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 ' | ||
+ | $letters = [char[]](0..255) -clike ' | ||
+ | $numbers = [char[]](0..255) -clike ' | ||
+ | # $special = @(" | ||
+ | |||
+ | |||
+ | $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 | ||
+ | </ | ||
+ | |||
+ | <code powershell> | ||
+ | # x = length in characters | ||
+ | # y = minimum number of non-alphanumeric characters | ||
+ | Add-Type -AssemblyName System.web | ||
+ | [System.Web.Security.Membership]:: | ||
+ | </ | ||
+ | ==== ФИО в логин ==== | ||
+ | <code powershell> | ||
+ | $src = Import-Excel ' | ||
+ | |||
+ | $alldata = @() | ||
+ | |||
+ | foreach ($fio in $src) { | ||
+ | $login = $fio -split " " | ||
+ | $f = $login[0] | ||
+ | $i = ($login[1]).substring(0, | ||
+ | $o = ($login[2]).substring(0, | ||
+ | $login = $f + $i + $o | ||
+ | |||
+ | # ьи, ье, ьё - Ильин, yuyu, yaya - Юлия Юрьевна | ||
+ | $login = $login -replace ' | ||
+ | -replace ' | ||
+ | -replace ' | ||
+ | -replace ' | ||
+ | -replace ' | ||
+ | -replace ' | ||
+ | -replace ' | ||
+ | -join "" | ||
+ | |||
+ | $raw = [pscustomobject]@{ | ||
+ | FIO = $fio | ||
+ | Login = $login | ||
+ | } | ||
+ | $alldata += $raw | ||
+ | } | ||
+ | $alldata | ||
+ | |||
+ | # $alldata |Export-Csv c: | ||
+ | </ | ||
+ | ==== Скопировать группы, | ||
+ | <code powershell> | ||
+ | [CmdletBinding()] | ||
+ | Param( | ||
+ | [Parameter(Mandatory=$True)] | ||
+ | $from, | ||
+ | [Parameter(Mandatory=$True)] | ||
+ | $to | ||
+ | ) | ||
+ | $CopyFromUser = Get-ADPrincipalGroupMembership $from |where {$_.name -like '*КТ *'} | ||
+ | $CopyToUser = Get-ADPrincipalGroupMembership | ||
+ | $CopyFromUser | Where{$CopyToUser -notcontains $_} | Add-ADGroupMember -Member $to | ||
+ | </ | ||
+ | ==== Добавить комп в группу ==== | ||
+ | <code powershell> | ||
+ | [CmdletBinding()] | ||
+ | Param( | ||
+ | [Parameter(Mandatory=$True)] | ||
+ | $comp | ||
+ | ) | ||
+ | add-ADGroupMember -Identity " | ||
+ | |||
+ | # Добавить много компов | ||
+ | " | ||
+ | (" | ||
+ | </ | ||
+ | |||
+ | ==== Показать включённых юзеров с датой входа более 2 недель ==== | ||
+ | <code powershell> | ||
+ | get-aduser -SearchBase " | ||
+ | ? Lastlogondate -lt (Get-Date).Adddays(-14) |sort lastlogondate -Descending |ft name, | ||
+ | </ | ||
+ | |||
+ | ==== Лог входов в систему ==== | ||
+ | <code powershell> | ||
+ | $file = " | ||
+ | $exist = Test-Path $file | ||
+ | $t = get-date -Format " | ||
+ | |||
+ | if ($exist -ne $true ) { | ||
+ | echo " | ||
+ | } | ||
+ | |||
+ | echo " | ||
+ | </ | ||
+ | Запуск из GPO: | ||
+ | <code dos> | ||
+ | %WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File \\path-to-server\scripts\logon-log.ps1 | ||
+ | </ | ||
+ | |||
+ | ==== Список учёток с неправильно заполненными инициалами ==== | ||
+ | <code powershell> | ||
+ | Get-ADUser -filter 'name -like " | ||
+ | ? initials -notmatch '\w\. \w\.' |select -expand name |sort | ||
+ | </ | ||
+ | |||
+ | ==== Поставить галку " | ||
+ | <code powershell> | ||
+ | Get-ADOrganizationalUnit -filter * |Set-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $true | ||
+ | </ | ||
+ | |||
+ | ===== Настройка локальных машин ===== | ||
+ | ==== Отключение энергосбережения для сетевого адаптера ==== | ||
+ | <code powershell> | ||
+ | # Disable "Allow the computer to turn off the device to save power" | ||
+ | foreach ($id in (gwmi win32_networkadapter |? {$_.physicaladapter -and $_.name -notmatch " | ||
+ | Set-ItemProperty " | ||
+ | } | ||
+ | |||
+ | # Вариант выборки сетевого адаптера: | ||
+ | # gwmi Win32_NetworkAdapter |? {$_.PNPDeviceID -notlike " | ||
+ | </ | ||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ==== Информация по TCP-соединениям ==== | ||
+ | <code powershell> | ||
+ | $comps = " | ||
+ | |||
+ | foreach ($comp in $comps) { | ||
+ | Invoke-Command -ComputerName $comp -ScriptBlock { | ||
+ | netstat -vb | ||
+ | } |Out-File c: | ||
+ | } | ||
+ | |||
+ | foreach ($comp in $comps) { | ||
+ | "--- $comp ---" | ||
+ | (gc c: | ||
+ | } | ||
+ | </ | ||
+ | < | ||
+ | --- PC1708 --- | ||
+ | Count Name Group | ||
+ | ----- ---- ----- | ||
+ | | ||
+ | 1 [1cv8c.exe] | ||
+ | 492 [Iwproxy.exe] | ||
+ | 1 [1cv8.exe] | ||
+ | 5 [OUTLOOK.EXE] | ||
+ | 244 [chrome.exe] | ||
+ | 4 [IWDeployAgent.exe] | ||
+ | 6 [iwdmc.exe] | ||
+ | 2 [iwshcs.exe] | ||
+ | 2 [LMS.exe] | ||
+ | 2 [DM.Client.exe] | ||
+ | --- PC1163 --- | ||
+ | 456 [Iwproxy.exe] | ||
+ | 3 [OUTLOOK.EXE] | ||
+ | 1 [spoolsv.exe] | ||
+ | 1 [1cv8c.exe] | ||
+ | 1 [1cv8.exe] | ||
+ | 228 [chrome.exe] | ||
+ | 4 [IWDeployAgent.exe] | ||
+ | 6 [iwdmc.exe] | ||
+ | 2 [iwshcs.exe] | ||
+ | 2 [DM.Client.exe] | ||
+ | </ | ||
+ | ===== Таблички, | ||
+ | <WRAP round important > | ||
+ | '' | ||
+ | Лучше использовать '' | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | </ | ||
+ | |||
+ | <code powershell> | ||
+ | $arrayList = [System.Collections.ArrayList]:: | ||
+ | $arrayList.add(" | ||
+ | </ | ||
+ | |||
+ | '' | ||
+ | |||
+ | <code powershell> | ||
+ | $a = [System.Collections.Generic.List[int]]:: | ||
+ | $a = [System.Collections.Generic.List[string]]:: | ||
+ | $a = [System.Collections.Generic.List[object]]:: | ||
+ | </ | ||
+ | ==== Добавить строку в отчёт с двумя столбцами ==== | ||
+ | <code powershell> | ||
+ | $report += New-Object -TypeName psobject -Property @{folder=" | ||
+ | </ | ||
+ | ==== Цвет затенения для строк ==== | ||
+ | Для использования в html для фонового цвета перемежающихся строк | ||
+ | |||
+ | * #EDEDED - очень светлый серый. | ||
+ | * #D9D9D9 - серый чуть потемнее. | ||
+ | * #DBDBDB - серый. У Экселя есть шаблон, | ||
+ | * #A5A5A5 - серый темнее. Лучше пользоваться уже белым шрифтом. | ||
+ | * #7B7B7B - тёмно-серый. У Экселя есть шаблон, | ||
+ | |||
+ | ==== Массив русского алфавита с индексом ==== | ||
+ | Почему не '' | ||
+ | <code powershell> | ||
+ | $alfabetRus = @(" | ||
+ | " | ||
+ | $alfabetRus |select @{n=" | ||
+ | |||
+ | letter index | ||
+ | ------ ----- | ||
+ | а 1 | ||
+ | б 2 | ||
+ | в 3 | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | Можно вычислить индексы символов и потом вставить их в массив для выбора | ||
+ | <code powershell> | ||
+ | $allchars = [char[]](0..65535) | ||
+ | ($allchars |select @{n=" | ||
+ | |||
+ | # А-Я | ||
+ | 1140..1071 | ||
+ | # а-я | ||
+ | 1072..1103 | ||
+ | # Ё,ё | ||
+ | 1025,1105 | ||
+ | # A-Z | ||
+ | 65..90 | ||
+ | # a-z | ||
+ | 97..122 | ||
+ | |||
+ | $alfabetRus = [char[]](1072..1077+1105+1078..1103) | ||
+ | $alfabetRus |select @{n=" | ||
+ | </ | ||
+ | |||
+ | <code powershell> | ||
+ | [char[]](1072..1103+1105) # русский алфавит строчные + буква ё | ||
+ | [char[]](1040..1071+1025) # русский алфавит заглавные + буква Ё | ||
+ | [char[]](1040..1103+1025+1105) # русский алфавит строчные и заглавные + буквы Ё и ё | ||
+ | </ | ||
+ | |||
+ | ==== Разбить массив на части определённого размера ==== | ||
+ | <code powershell> | ||
+ | $list = 1..1018 | ||
+ | $chunkSize = 100 | ||
+ | |||
+ | $c = [pscustomobject]@{Value = 0} | ||
+ | $groups = $list |group {[math]:: | ||
+ | |||
+ | $groups[0].group | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ===== Файловые операции ===== | ||
+ | ==== Удалять из папки все файлы старше текущего месяца, | ||
+ | <code powershell> | ||
+ | $path=" | ||
+ | $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 ==== | ||
+ | <code powershell> | ||
+ | gci | sort CreationTime | select -skip 10 | Remove-item -Force | ||
+ | </ | ||
+ | |||
+ | ==== Пакетное переименование файлов ==== | ||
+ | Дано:\\ | ||
+ | Pet Shop Boys - Happiness-47208322.mp3\\ | ||
+ | Pet Shop Boys - The Pop Kids-47208325.mp3 | ||
+ | |||
+ | <code powershell> | ||
+ | [int]$n = 1 | ||
+ | $files = dir ' | ||
+ | foreach($file in $files) { | ||
+ | $a = $n.ToString(" | ||
+ | Rename-Item $file -NewName ($file.name -replace " | ||
+ | $n = $n+1 | ||
+ | } | ||
+ | dir |Rename-Item -NewName {$_.name -replace " | ||
+ | |||
+ | # сделать плейлист с именем как у текущего каталога | ||
+ | $plsname = (Get-ItemProperty .).name | ||
+ | (dir -file *.mp3).name |out-file .\" | ||
+ | </ | ||
+ | Результат: | ||
+ | 01 Pet Shop Boys - Happiness.mp3\\ | ||
+ | 02 Pet Shop Boys - The Pop Kids.mp3 | ||
+ | |||
+ | https:// | ||
+ | |||
+ | Дано:\\ | ||
+ | 1 - Introduction.mp4\\ | ||
+ | 10 - Lesson 1 Lab.mp4\\ | ||
+ | 100 - Understanding Why Decoupling is Important.mp4 | ||
+ | |||
+ | <code powershell> | ||
+ | $files = dir ' | ||
+ | |||
+ | foreach ($file in $files) { | ||
+ | $num,$rest = $file.name -split ' - ',2 | ||
+ | Rename-Item $file.FullName -NewName " | ||
+ | } | ||
+ | </ | ||
+ | Результат: | ||
+ | 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 | ||
+ | и т. д. | ||
+ | </ | ||
+ | Чтобы отсортировать по-человечески: | ||
+ | <code powershell> | ||
+ | dir " | ||
+ | </ | ||
+ | |||
+ | ==== Посмотреть квоты на файловом сервере ==== | ||
+ | <code powershell> | ||
+ | Get-FSRMQuota |? path -Match ' | ||
+ | </ | ||
+ | |||
+ | ==== Ожидание снятия блокировки файла и копирование ==== | ||
+ | <code powershell> | ||
+ | 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]:: | ||
+ | if ($oStream) { | ||
+ | $oStream.Close() | ||
+ | } | ||
+ | return $false | ||
+ | } catch { | ||
+ | return $true # file is locked by a process | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $from = ' | ||
+ | $to = ' | ||
+ | |||
+ | do { | ||
+ | sleep 30 | ||
+ | } | ||
+ | until (-not (Test-FileLock $from)) | ||
+ | |||
+ | Write-Host -fore green " | ||
+ | cp $from $to | ||
+ | |||
+ | # Test-Path " | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Создание тестовых файлов с датами изменения и создания в прошлом ==== | ||
+ | <code powershell> | ||
+ | $t = Get-Date | ||
+ | $folder = " | ||
+ | |||
+ | 1..100 |% { | ||
+ | $fileTime = $t.AddDays(-$_) | ||
+ | $fileBaseName = $fileTime.ToString(" | ||
+ | $filePath = " | ||
+ | |||
+ | $file = New-Item $filePath -ItemType File | ||
+ | Set-Content $filePath -Value $fileBaseName | ||
+ | |||
+ | $file.CreationTime = $fileTime | ||
+ | $file.LastWriteTime = $fileTime | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Удалить файл из корзины ==== | ||
+ | <code powershell> | ||
+ | $file = ' | ||
+ | |||
+ | (New-Object -ComObject Shell.Application).NameSpace(0x0a).Items() |? { | ||
+ | ($_.ExtendedProperty(" | ||
+ | } |del | ||
+ | </ | ||
+ | |||
+ | ==== Копирование файлов robocopy + splatting ==== | ||
+ | Подстановка значений, | ||
+ | <code powershell> | ||
+ | $ext = @((Read-Host " | ||
+ | robocopy " | ||
+ | </ | ||
+ | ===== Работа с текстом ===== | ||
+ | Программа для создания большого файла txt: [[https:// | ||
+ | ==== Конвертировать строку во время ==== | ||
+ | <code powershell> | ||
+ | $folder = " | ||
+ | $reportDate = [datetime]:: | ||
+ | |||
+ | 25 октября 2021 г. 0:00:00 | ||
+ | </ | ||
+ | https:// | ||
+ | [[https:// | ||
+ | |||
+ | ==== Убрать из файла все строки, | ||
+ | <code powershell> | ||
+ | (Get-Content " | ||
+ | </ | ||
+ | http:// | ||
+ | |||
+ | ==== Каждое слово с большой буквы ==== | ||
+ | <code powershell> | ||
+ | (Get-Culture).TextInfo.ToTitleCase(' | ||
+ | </ | ||
+ | ==== Фильтр отчётов Cobian Backup ==== | ||
+ | <code powershell> | ||
+ | cd D: | ||
+ | Expand-Archive .\Журналы.zip | ||
+ | cd .\Журналы | ||
+ | gc *.txt |Select-String ERR |out-file -width 30000 .\errors.txt | ||
+ | Invoke-Item .\errors.txt | ||
+ | </ | ||
+ | |||
+ | ==== Выбрать каждую N строку в файле ==== | ||
+ | <code powershell> | ||
+ | # каждую 1, 5, 9, 13, 17 и т. д. | ||
+ | gc " | ||
+ | </ | ||
+ | |||
+ | ==== Замена ошибок в тексте (afterscan) ==== | ||
+ | FIXME | ||
+ | <code powershell> | ||
+ | -replace ' | ||
+ | -replace ' | ||
+ | -replace " | ||
+ | -replace " | ||
+ | -replace ' | ||
+ | -replace " | ||
+ | |||
+ | -replace ' | ||
+ | </ | ||
+ | |||
+ | ==== Обработка больших файлов ==== | ||
+ | Надо применять методы .NET и упаковывать regex, т. к. '' | ||
+ | <code powershell> | ||
+ | function ExtractTo-Chunk ($in, | ||
+ | [regex]$regex = $regex | ||
+ | [System.IO.File]:: | ||
+ | } | ||
+ | |||
+ | ExtractTo-Chunk -in " | ||
+ | ExtractTo-Chunk -in " | ||
+ | </ | ||
+ | |||
+ | |||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | |||
+ | Быстрое чтение файла в массив: | ||
+ | <code powershell> | ||
+ | [io.file]:: | ||
+ | [io.file]:: | ||
+ | [io.file]:: | ||
+ | </ | ||
+ | |||
+ | ==== Склонение слов в предложении с числительным ==== | ||
+ | <code powershell> | ||
+ | function Get-Declension ($pre1, | ||
+ | switch -Regex ($n) { | ||
+ | ' | ||
+ | ' | ||
+ | default | ||
+ | } | ||
+ | if ($pre1) {"$p $n $w"} | ||
+ | else {"$n $w"} | ||
+ | } | ||
+ | |||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | Машина проехала 2 километра | ||
+ | Одобрено 101 обновление | ||
+ | Всего 22 активных пользователя | ||
+ | Удален 221 каталог | ||
+ | Удалено 11 папок | ||
+ | Посажено 2511 дубов | ||
+ | Разбито 4 яйца | ||
+ | </ | ||
+ | |||
+ | ==== Список модулей PHP из буфера по алфавиту и с переносом строки ==== | ||
+ | <code powershell> | ||
+ | $list = (Get-Clipboard) -replace ' | ||
+ | $total = $list.count | ||
+ | $c = 1 | ||
+ | $list |% { | ||
+ | if ($c -eq $total) {$_} | ||
+ | else {$_ + " \"} | ||
+ | $c++ | ||
+ | } |Set-Clipboard | ||
+ | </ | ||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | Исходный список | ||
+ | < | ||
+ | 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 | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | Результат | ||
+ | < | ||
+ | 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: | ||
+ | border: 1px solid #666666; background:# | ||
+ | ... | ||
+ | overflow: | ||
+ | background:# | ||
+ | |||
+ | Путь размещения параметров: | ||
+ | |||
+ | Объяснение | ||
+ | </ | ||
+ | |||
+ | <code powershell> | ||
+ | (gc ' | ||
+ | </ | ||
+ | |||
+ | ==== Замена нескольких пустых строк на одну ==== | ||
+ | <code powershell> | ||
+ | (gc ' | ||
+ | </ | ||
+ | |||
+ | ==== Преобразование строк в таблицу ==== | ||
+ | < | ||
+ | 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 | ||
+ | |||
+ | </ | ||
+ | |||
+ | Вычистить ненужные строки и мусор, разделить текст на сегменты, | ||
+ | <code powershell> | ||
+ | (Get-Content .\opera.adr -Raw) -replace ' | ||
+ | $props = ConvertFrom-StringData -StringData ($_ -replace ' | ||
+ | New-Object PSOBject -Property $props |select Name, Mail | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ==== Поиск значений ASCII для символов ==== | ||
+ | |||
+ | <code powershell> | ||
+ | [char[]]" | ||
+ | |||
+ | Char Code | ||
+ | ---- ---- | ||
+ | а 1072 | ||
+ | б 1073 | ||
+ | в 1074 | ||
+ | г 1075 | ||
+ | д 1076 | ||
+ | я 1103 | ||
+ | ё 1105 | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== json ==== | ||
+ | Удобное создание json | ||
+ | <code powershell> | ||
+ | $json = @{ | ||
+ | repoName = " | ||
+ | name = " | ||
+ | version = " | ||
+ | } |ConvertTo-Json -Compress | ||
+ | # Результат: | ||
+ | |||
+ | # Применение, | ||
+ | $response = Invoke-RestMethod -Uri " | ||
+ | -Headers @{" | ||
+ | -Method Post -Body $json -ContentType " | ||
+ | |||
+ | </ | ||
+ | |||
+ | ===== Графика ===== | ||
+ | ==== Диаграммы ==== | ||
+ | На примере ежечасно снимаемых логов вошедших по VPN пользователей. | ||
+ | <code powershell> | ||
+ | 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 = ' | ||
+ | |||
+ | $ChartArea = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.ChartArea | ||
+ | $ChartArea.AxisX.Title = ' | ||
+ | $ChartArea.AxisY.Title = ' | ||
+ | $ChartArea.AxisX.Interval = ' | ||
+ | $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(' | ||
+ | $Chart.Series[' | ||
+ | |||
+ | # Chart type - Column | ||
+ | $Chart.Series[' | ||
+ | |||
+ | # Looping files | ||
+ | $txtFiles |% { | ||
+ | $hour = $_.BaseName -replace " | ||
+ | $usersCount = (gc $_.FullName |% {($_ -split "'" | ||
+ | $Value = $Chart.Series[' | ||
+ | } | ||
+ | |||
+ | # Title | ||
+ | $Title = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Title | ||
+ | $Chart.Titles.Add($Title) | ||
+ | $Chart.Titles[0].Text = ' | ||
+ | |||
+ | # Saving PNG file | ||
+ | $png = " | ||
+ | $Chart.SaveImage(" | ||
+ | </ | ||
+ | |||
+ | {{: | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ===== Интернет ===== | ||
+ | ==== Скачать файлы по списку ==== | ||
+ | <code powershell> | ||
+ | $url = " | ||
+ | $pattern = " | ||
+ | (curl $url).links |select -expand href |Select-String -SimpleMatch " | ||
+ | % { | ||
+ | $f = $url + $_ | ||
+ | curl $f -OutFile " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Отправить письмо ==== | ||
+ | Без запроса пароля. | ||
+ | <code powershell> | ||
+ | # Сохранение шифрованного пароля в файл | ||
+ | Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File C: | ||
+ | |||
+ | $pass = cat C: | ||
+ | $mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist " | ||
+ | # С Яндекса | ||
+ | Send-MailMessage -SmtpServer smtp.yandex.ru -UseSsl -Encoding utf8 -From " | ||
+ | </ | ||
+ | |||
+ | ==== Игнорировать самоподписанный сертификат SMTP ==== | ||
+ | <code powershell> | ||
+ | [System.Net.ServicePointManager]:: | ||
+ | Send-MailMessage -SmtpServer " | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Письмо с картинкой в тексте ==== | ||
+ | <code powershell> | ||
+ | Send-MailMessage -To " | ||
+ | -SmtpServer " | ||
+ | -Body " | ||
+ | < | ||
+ | < | ||
+ | <img src=' | ||
+ | < | ||
+ | " -Attachments " | ||
+ | </ | ||
+ | https:// | ||
+ | ==== Сколько дней осталось до окончания действия SSL-сертификата ==== | ||
+ | С помощью curl (надо [[https:// | ||
+ | <code powershell> | ||
+ | $urls = @( | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | #$urls = gc " | ||
+ | $t = get-date | ||
+ | |||
+ | function Decode-UnicodeEscaped { | ||
+ | param([string]$str) | ||
+ | # decode if unicode escaped strings | ||
+ | # https:// | ||
+ | [Regex]:: | ||
+ | {[char]:: | ||
+ | } | ||
+ | |||
+ | $report = @() | ||
+ | $urls |% { | ||
+ | $dump = & C: | ||
+ | $issuer = $dump -match " | ||
+ | $expdate = $dump -match " | ||
+ | $expdate = Get-Date $("{0} {3} {1} {2} {4}" -f " | ||
+ | $obj = [pscustomobject]@{ | ||
+ | Сайт = $_ | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | } | ||
+ | $report += $obj | ||
+ | Remove-Variable dump, | ||
+ | } | ||
+ | $report |sort ' | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | Рабочие методы в чистом Powershell: https:// | ||
+ | Ранее работавший метод: https:// | ||
+ | |||
+ | ==== Получить HTML-объекты определённого класса ==== | ||
+ | <code powershell> | ||
+ | $url = " | ||
+ | # Нужно указать первый элемент [0], даже если он единственный. | ||
+ | (iwr $url).ParsedHtml.body.getElementsByClassName(' | ||
+ | </ | ||
+ | https:// | ||
+ | ===== Информация ===== | ||
+ | Вывод системной информации: | ||
+ | <code powershell> | ||
+ | # Computer name | ||
+ | $comp = $env: | ||
+ | # login | ||
+ | $login = $env: | ||
+ | # Full user name | ||
+ | $fullusername = ([adsi]" | ||
+ | # IP address | ||
+ | $ip = Get-NetIPAddress -AddressFamily IPv4 | where {$_.interfacealias -NotLike ' | ||
+ | # MAC | ||
+ | $mac = Get-NetAdapter -Physical | select macaddress -ExpandProperty macaddress | ||
+ | # IP и MAC для PoSH старых версий | ||
+ | # Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter " | ||
+ | # Time mark | ||
+ | $mark = get-date -UFormat " | ||
+ | |||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | echo "IP: $ip" | ||
+ | echo "MAC: $mac" | ||
+ | echo " | ||
+ | </ | ||
+ | |||
+ | ==== Список установленных программ ==== | ||
+ | <code powershell> | ||
+ | # Узнать удалённо, | ||
+ | Invoke-Command -cn server1 -ScriptBlock {Get-ItemProperty HKLM: | ||
+ | # Для 32-битных приложений на 64-битной ОС: | ||
+ | # HKLM: | ||
+ | |||
+ | # Вариант через Get-WmiObject (медленно) | ||
+ | gwmi win32_product -ComputerName server1 -Filter "Name like ' | ||
+ | </ | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | Универсальный вариант: | ||
+ | |||
+ | |||
+ | ==== Создание окон ==== | ||
+ | <code powershell> | ||
+ | [System.Reflection.Assembly]:: | ||
+ | [System.Windows.Forms.MessageBox]:: | ||
+ | </ | ||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | Первое число: | ||
+ | 0 OK\\ | ||
+ | 1 OKCancel\\ | ||
+ | 2 AbortRetryIgnore\\ | ||
+ | 3 YesNoCancel\\ | ||
+ | 4 YesNo\\ | ||
+ | 5 RetryCancel | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | Второе число: | ||
+ | 0 None\\ | ||
+ | 16 Hand\\ | ||
+ | 16 Error\\ | ||
+ | 16 Stop\\ | ||
+ | 32 Question\\ | ||
+ | 48 Exclamation\\ | ||
+ | 48 Warning\\ | ||
+ | 64 Asterisk\\ | ||
+ | 64 Information | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ==== Температура процессора ==== | ||
+ | <code powershell> | ||
+ | ((gwmi -Namespace root\wmi MSAcpi_ThermalZoneTemperature).CurrentTemperature |sort |select -last 1) / 100 | ||
+ | </ | ||
+ | CMD: | ||
+ | <code dos> | ||
+ | @echo off | ||
+ | for /f " | ||
+ | echo %HunDegCel: | ||
+ | pause | ||
+ | </ | ||
+ | ==== Аптайм ==== | ||
+ | <code powershell> | ||
+ | read-host " | ||
+ | gcim win32_operatingsystem | ||
+ | select Caption, | ||
+ | </ | ||
+ | |||
+ | ==== Даты установки Windows ==== | ||
+ | <code powershell> | ||
+ | $report = gci -Path HKLM: | ||
+ | Get-ItemProperty -Path Registry:: | ||
+ | select ProductName, | ||
+ | @{n=" | ||
+ | sort {[int]($_.CurrentBuild)} | ||
+ | |||
+ | $report += gcim Win32_OperatingSystem |select @{n=' | ||
+ | |||
+ | $report | ||
+ | |||
+ | ProductName | ||
+ | ----------- | ||
+ | Windows 10 Pro | ||
+ | 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 | ||
+ | </ | ||
+ | |||
+ | ==== Состояние диска ==== | ||
+ | FIXME | ||
+ | Добавить проверку и отправку писем, если статус не ОК | ||
+ | <code powershell> | ||
+ | $i = gcim Win32_DiskDrive |? Partitions -gt 0 | select SystemName, | ||
+ | if ($i.Status -ne " | ||
+ | </ | ||
+ | |||
+ | ==== Total LBAs Written в мегабайты/ | ||
+ | <code powershell> | ||
+ | [single]$lbaWritten = Read-Host " | ||
+ | [single]$lbaSize = Read-Host " | ||
+ | if (-not $lbaSize) {$lbaSize = 512} | ||
+ | $bytes = $lbaWritten * $lbaSize | ||
+ | |||
+ | Write-Host -fore Yellow " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | </ | ||
+ | |||
+ | ==== Сокращение десятичных знаков, | ||
+ | <code powershell> | ||
+ | @{n=' | ||
+ | [math]:: | ||
+ | 0,43 | ||
+ | </ | ||
+ | ==== Внешний IP ==== | ||
+ | <code powershell> | ||
+ | curl https:// | ||
+ | </ | ||
+ | |||
+ | ==== Поиск свободного IP в сети ==== | ||
+ | <code powershell> | ||
+ | 1..254 |% { | ||
+ | $ip = " | ||
+ | if (-not (Test-Connection $ip -Count 1 -Quiet)) { | ||
+ | $ip | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ==== Потребление памяти несколькими одноимёнными процессами ==== | ||
+ | <code powershell> | ||
+ | get-process firefox |group name |select name, | ||
+ | </ | ||
+ | |||
+ | ==== Пути каталогов в профиле и системе ==== | ||
+ | <code powershell> | ||
+ | # Перечислить | ||
+ | [enum]:: | ||
+ | AdminTools | ||
+ | ApplicationData | ||
+ | CDBurning | ||
+ | CommonAdminTools | ||
+ | CommonApplicationData | ||
+ | CommonDesktopDirectory | ||
+ | CommonDocuments | ||
+ | CommonMusic | ||
+ | ... | ||
+ | # Вывести путь | ||
+ | [environment]:: | ||
+ | C: | ||
+ | </ | ||
+ | |||
+ | ===== Реестр ===== | ||
+ | <code powershell> | ||
+ | $regPath = " | ||
+ | New-ItemProperty -Path $regPath -Name JunkMailImportLists -PropertyType Dword -Value 1 -Force > $null | ||
+ | </ | ||
+ | |||
+ | Список значений '' | ||
+ | < | ||
+ | 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:// | ||
+ | ===== Прочее ===== | ||
+ | ==== Таймер ==== | ||
+ | <code powershell> | ||
+ | $timer = [system.diagnostics.stopwatch]:: | ||
+ | [math]:: | ||
+ | $timer.IsRunning | ||
+ | True | ||
+ | $timer.Stop() | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Секунды в dd.hh:mm:ss ==== | ||
+ | <code powershell> | ||
+ | [timespan]:: | ||
+ | 3.21:30:21 | ||
+ | </ | ||
+ | ==== Текст в речь ==== | ||
+ | <code powershell> | ||
+ | $currentdate = (get-date).ToLongDateString() | ||
+ | $currenttime = (get-date).ToLongTimeString() | ||
+ | Add-Type -AssemblyName System.speech | ||
+ | $speak = New-Object System.Speech.Synthesis.SpeechSynthesizer | ||
+ | $speak.Speak(" | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Скриншот ==== | ||
+ | <code powershell> | ||
+ | [void][reflection.assembly]:: | ||
+ | [system.windows.forms.sendkeys]:: | ||
+ | Get-Clipboard -Format Image | ForEach-Object -MemberName Save -ArgumentList " | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Установить/ | ||
+ | Windows: | ||
+ | <code powershell> | ||
+ | iex "& { $(irm https:// | ||
+ | </ | ||
+ | Linux: | ||
+ | <code bash> | ||
+ | wget https:// | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Запуск скрипта Powershell из-под PSExec ==== | ||
+ | <code dos> | ||
+ | @echo off | ||
+ | title Install Powershell 5.1 | ||
+ | set /P COMP=computername: | ||
+ | psexec.exe \\%COMP% /s /h cmd /c powershell -executionpolicy bypass -file " | ||
+ | </ | ||
+ | |||
+ | ==== Обработка объектов, | ||
+ | <code powershell> | ||
+ | # Ярлык | ||
+ | powershell -f " | ||
+ | # вариант | ||
+ | powershell -noexit -noprofile -f " | ||
+ | </ | ||
+ | При перетаскивании на ярлык в '' | ||
+ | <code powershell> | ||
+ | # Получить список файлов | ||
+ | $inputFiles = @() | ||
+ | $args |% { | ||
+ | if ((get-item " | ||
+ | $inputFiles += dir " | ||
+ | } | ||
+ | else { | ||
+ | $inputFiles += get-item " | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Out-Null ==== | ||
+ | Никогда не нужно пользоваться командлетом Out-Null, он тормозной. Вместо него (по убыванию быстродействия) | ||
+ | <code powershell> | ||
+ | $array = New-Object -TypeName System.Collections.ArrayList | ||
+ | |||
+ | # $null (assigned to $null) - RECOMMENDED | ||
+ | $null = $array.Add(' | ||
+ | |||
+ | # [void] (cast to void) - RECOMMENDED | ||
+ | [void]$array.Add(' | ||
+ | |||
+ | # $null (redirected to $null) | ||
+ | $array.Add(' | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Путь к файлу процесса на удалённой машине ==== | ||
+ | '' | ||
+ | <code powershell> | ||
+ | Get-WmiObject -Class win32_process -ComputerName $MyServer -Filter 'name like " | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Работа с PDF ==== | ||
+ | https:// | ||
+ | |||
+ | https:// |