Порой мне приходится писать скрипты PowerShell для запроса различной информации.
Для того, чтобы выводимая информация выглядела так, как мне нужно, я обычно использую некоторые приемы. О них и пойдет речь.
Первые из них достаточно просты, однако по мере приближения к концу поста, сложность будет возрастать.
В качестве примеров используются запросы, касающиеся файловых томов и общих папок, однако приводимые приемы могут быть использованы в самых разных ситуациях.
1. Использование -AutoSize
Мне не слишком нравится стандартный вид результатов запросов в PowerShell. Я предпочитаю, когда колонки расположены в левой части экрана. Это именно то, что делает параметр -AutoSize командлета Format-Table (ft — его алиас).
Как следует из названия параметра, его использование приводит к тому, что ширина колонок подгоняется в ширине выводимых данных, что также позволяет избежать вывода символа многоточия в конце строки.
PS C:\> Get-SmbShare
Name ScopeName Path Description
—- ——— —- ————
ADMIN$ * C:\windows Remote Admin
BackupFiles * E:\Backup Project backup files
C$ * C:\ Default share
D$ * D:\ Default share
F$ * F:\ Default share
Images * D:\Images Pictures to be used in pro…
IPC$ * Remote IPC
print$ * C:\windows\system32\spool... Printer Drivers
Projects * C:\Projects Recent project files
PS C:\> Get-SmbShare | FT -AutoSize
Name ScopeName Path Description
—- ——— —- ————
ADMIN$ * C:\windows Remote Admin
BackupFiles * E:\Backup Project backup files
C$ * C:\ Default share
D$ * D:\ Default share
F$ * F:\ Default share
Images * D:\Images Pictures to be used in projects
IPC$ * Remote IPC
print$ * C:\windows\system32\spool\drivers Printer Drivers
Projects * C:\Projects Recent project files
2. Выбираем нужные колонки
Каждый объект имеет свой собственный формат вывода по умолчанию, с определенным набором колонок. Если вас это не устраивает, вы можете указать те свойства, которые вы хотели бы видеть. Для того, чтобы узнать, какими свойствами обладает определенный объект, вы можете воспользоваться командами Select * или Get-Member.
PS C:\> Get-SmbShare | Select * -First 1
PresetPathAcl :
ShareState : Online
AvailabilityType : NonClustered
ShareType : FileSystemDirectory
FolderEnumerationMode : Unrestricted
CachingMode : Manual
SmbInstance : Default
CATimeout : 0
ConcurrentUserLimit : 0
ContinuouslyAvailable : False
CurrentUsers : 0
Description : Remote Admin
EncryptData : False
Name : ADMIN$
Path : C:\windows
Scoped : False
ScopeName : *
SecurityDescriptor : O:SYG:SYD:(A;;GA;;;BA)(A;;GA;;;BO)(A;;GA;;;IU)
ShadowCopy : False
Special : True
Temporary : False
Volume : \?\Volume{4304337d-6763-11e3-8255-806e6f6e6963}\
PSComputerName :
CimClass : ROOT/Microsoft/Windows/SMB:MSFT_SmbShare
CimInstanceProperties : {AvailabilityType, CachingMode, CATimeout, ConcurrentUserLimit…}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
PS C:\> Get-SmbShare | Get-Member
TypeName: Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/SMB/MSFT_SmbShare
Name MemberType Definition
—- ———- ———-
Clone Method System.Object ICloneable.Clone()
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Object obj)
GetCimSessionComputerName Method string GetCimSessionComputerName()
GetCimSessionInstanceId Method guid GetCimSessionInstanceId()
GetHashCode Method int GetHashCode()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, Sys…
GetType Method type GetType()
ToString Method string ToString()
CATimeout Property uint32 CATimeout {get;set;}
ConcurrentUserLimit Property uint32 ConcurrentUserLimit {get;set;}
ContinuouslyAvailable Property bool ContinuouslyAvailable {get;set;}
CurrentUsers Property uint32 CurrentUsers {get;set;}
Description Property string Description {get;set;}
EncryptData Property bool EncryptData {get;set;}
Name Property string Name {get;}
Path Property string Path {get;}
PSComputerName Property string PSComputerName {get;}
Scoped Property bool Scoped {get;}
ScopeName Property string ScopeName {get;}
SecurityDescriptor Property string SecurityDescriptor {get;set;}
ShadowCopy Property bool ShadowCopy {get;}
Special Property bool Special {get;}
Temporary Property bool Temporary {get;}
Volume Property string Volume {get;}
AvailabilityType ScriptProperty System.Object AvailabilityType {get=[Microsoft.PowerShell.Cmdletization.Gen…
CachingMode ScriptProperty System.Object CachingMode {get=[Microsoft.PowerShell.Cmdletization.Generate…
FolderEnumerationMode ScriptProperty System.Object FolderEnumerationMode {get=[Microsoft.PowerShell.Cmdletizatio…
PresetPathAcl ScriptProperty System.Object PresetPathAcl {get=$acl = Get-Acl ($this.PSBase.CimInstancePr…
ShareState ScriptProperty System.Object ShareState {get=[Microsoft.PowerShell.Cmdletization.Generated…
ShareType ScriptProperty System.Object ShareType {get=[Microsoft.PowerShell.Cmdletization.GeneratedT…
SmbInstance ScriptProperty System.Object SmbInstance {get=[Microsoft.PowerShell.Cmdletization.Generate…
PS C:\> Get-SmbShare | FT Name, Path, Special, AvailabilityType -AutoSize
Name Path Special AvailabilityType
—- —- ——- —————-
ADMIN$ C:\windows True NonClustered
BackupFiles E:\Backup False NonClustered
C$ C:\ True NonClustered
D$ D:\ True NonClustered
F$ F:\ True NonClustered
Images D:\Images False NonClustered
IPC$ True NonClustered
print$ C:\windows\system32\spool\drivers False NonClustered
Projects C:\Projects False NonClustered
3. Фильтруем строки таблицы
Вы можете производит отбор объектов на основании значений любых их свойств. Для этого вам могут понадобиться такие операторы как равно (-eq), не равно (-ne), больше чем (-gt), операторы сравнения по шаблону (-like, -notlike) и многие другие.
Для фильтрации объектов используется командлет Where-Object. Вы также можете указать один из его алиасов — where, или вопросительный знак — ?. Начнем с простого примера, затем перейдем к более сложным запросам.
Для вывода всех общих папок, не являющихся специальными, можно использовать одну из следующих команд:
Get-SmbShare | Where-Object {$_.Special –ne $true}
Get-SmbShare | ? Special –ne $true
Если для сравнения вы используете выражение в фигурных скобках, для ссылки на текущий объект вам потребуется указать $_.имя-свойства. При использовании вида команды без фигурных скобок, вы можете указывать просто имя свойства.
Теперь более сложный пример. В нем мы выведем список общих папок, не являющихся специальными, чье описание начинается с символа «P».
Get-SmbShare | ? { $.Special -ne $true -and $.Description -like «P*» }
PS C:\> Get-SmbShare | Where-Object {$_.Special –ne $true} | FT -AutoSize
Name ScopeName Path Description
—- ——— —- ————
BackupFiles * E:\Backup Project backup files
Images * D:\Images Pictures to be used in projects
print$ * C:\windows\system32\spool\drivers Printer Drivers
Projects * C:\Projects Recent project files
PS C:\> Get-SmbShare | ? Special –ne $true| FT -AutoSize
Name ScopeName Path Description
—- ——— —- ————
BackupFiles * E:\Backup Project backup files
Images * D:\Images Pictures to be used in projects
print$ * C:\windows\system32\spool\drivers Printer Drivers
Projects * C:\Projects Recent project files
PS C:\> Get-SmbShare | ? Special | FT -AutoSize
Name ScopeName Path Description
—- ——— —- ————
ADMIN$ * C:\windows Remote Admin
C$ * C:\ Default share
D$ * D:\ Default share
F$ * F:\ Default share
IPC$ * Remote IPC
PS C:\> Get-SmbShare | ? { $.Special -ne $true -and $.Description -like «P*» } | FT -AutoSize
Name ScopeName Path Description
—- ——— —- ————
BackupFiles * E:\Backup Project backup files
Images * D:\Images Pictures to be used in projects
print$ * C:\windows\system32\spool\drivers Printer Drivers
4. Создаем собственные столбцы
В добавок к возможности выбора, какие колонки мы хотим видеть среди результатов, мы также можем создавать произвольные таблицы с использованием любых выражений. Это может быть полезным при создании значений колонок, вычисляемых на основании текущих данных.
Также вы можете использовать эту возможность для переименования существующих значений (в случае, если вам не нравятся имена по умолчанию) и для задания таких параметров столбцов, как выравнивание и ширина.
Также как и в случае использования формата командлета Where-Object с фигурными скобками, выражение должно быть заключено в символы {}, а для ссылки на текущий объект будет использоваться $_.
Синтаксис команды использует хеш-таблицу, что может показаться несколько непривычным. В хеш-таблице как минимум должны быть указаны Label (или Name) — имя колонки, и Expression — выражение. Кроме этого могут быть указаны Alignment (или Align) — выравнивание, и Width — ширина.
Пример переименования колонки из Path в Folder.
Get-SmbShare | FT Name, @{ Label=”Folder”; Expression={$_.Path} }, Description –AutoSize
Еще один прмер, в котором в вместо всего пути выводится только буква диска:
Get-SmbShare | FT Name, Description, @{ Name=”Drive”; Expression={$_.Path.Substring(0,1)}; Alignment=”Center” } –AutoSize
И еще один, в котором для общих папок, содержащих знак доллара в конце имени, в столбце Hidden указывается Yes.
Get-SmbShare | FT Name, Path, @{ Align=»Center»; Expression={If ($_.Name –like “*$”) {“Yes”} else {“No”} }; Label=”Hidden” } –AutoSize
Да, я знаю, тут много фигурных скобок. Просто постарайтесь не потеряться в них.
PS C:\> Get-SmbShare | FT Name, @{ Label=”Folder”; Expression={$_.Path} }, Description –AutoSize
Name Folder Description
—- —— ————
ADMIN$ C:\windows Remote Admin
BackupFiles E:\Backup Project backup files
C$ C:\ Default share
D$ D:\ Default share
F$ F:\ Default share
Images D:\Images Pictures to be used in projects
IPC$ Remote IPC
print$ C:\windows\system32\spool\drivers Printer Drivers
Projects C:\Projects Recent project files
PS C:\> Get-SmbShare | FT Name, Description, @{ Name=”Drive”; Expression={$_.Path.Substring(0,1)}; Alignment=”Center” } –AutoSize
Name Description Drive
—- ———— ——
ADMIN$ Remote Admin C
BackupFiles Project backup files E
C$ Default share C
D$ Default share D
F$ Default share F
Images Pictures to be used in projects D
IPC$ Remote IPC
print$ Printer Drivers C
Projects Recent project files C
PS C:\> Get-SmbShare | FT Name, Path, @{ Align=»Center»; Expression={If ($_.Name –like “*$”) {“Yes”} else {“No”} }; Label=”Hidden” } –AutoSize
Name Path Hidden
—- —- ——
ADMIN$ C:\windows Yes
BackupFiles E:\Backup No
C$ C:\ Yes
D$ D:\ Yes
F$ F:\ Yes
Images D:\Images No
IPC$ Yes
print$ C:\windows\system32\spool\drivers Yes
Projects C:\Projects No
PS C:\> Get-Volume | Sort DriveLetter | FT DriveType, DriveLetter, FileSystem, @{Expression={ [int] ($.Size/1MB) }; Label=»Total (MB)»; Align=»Right» }, @{Expression={ [int] (($.Size-$.SizeRemaining)/1MB) }; Label=»Used (MB)»; Align=»Right» }, @{Expression={ [int] ($.SizeRemaining/1MB) }; Label=»Free (MB)»; Align=»Right» }, @{Expression={ int }; Label=»Free %»; Align=»Right» } –AutoSize
DriveType DriveLetter FileSystem Total (MB) Used (MB) Free (MB) Free %
Fixed NTFS 350 287 63 18
Fixed C NTFS 243845 214308 29537 12
Fixed D NTFS 476937 56928 420009 88
Removable E NTFS 125903 20925 104978 83
Fixed F NTFS 476938 137181 339757 71
PS C:\> dir d:*.* -Recurse | Select @{Expression={$.CreationTime.Year};Label=»YearCreated»}, @{Expression={$.CreationTime.Month};Label=»MonthCreated»} | Group YearCreated, MonthCreated | Select @{Expression={$.Name.Split(«,»)[0].Trim()};Label=»Year»}, @{Expression={$.Name.Split(«,»)[1].Trim()};Label=»Month»}, Count | Sort Count -Descending | FT Year, Month, Count -AutoSize
Year Month Count
2013 7 10172
2013 8 9114
2013 10 9097
2013 9 4075
2014 2 483
2013 4 47
2012 2 16
2013 11 15
2013 6 5
2013 12 4
2014 4 3
2013 5 2
2012 12 2
2012 5 1
5. Изменение формата вывода числовых данных
Использование выражений позволяет нам изменять формат выводимых данных. Например — чисел. Для этого используется оператор -f, с помощью которого вы можете указывать разделитель разрядов числа, количество знаков после запятой, денежный или процентный форматы.
Строка может включать в себя ссылки на несколько чисел, поэтому вам нужно указывать индекс числа в фигурных скобках. Следующий пример выводит три различных числа.
PS C:\> «Note that {1} times {0} equals {2}» -f 123, 2, 246
Note that 2 times 123 equals 246
Кроме индекса, после символа двоеточия вы можете указать букву, определяющую, какой тип форматирования использовать, за которой следует указатель количества цифр после запятой. Тип форматирования включает: C — денежный формат, N — числовой, P — процентный, E — экспоненциальный.
PS C:\> «They spent {0:C2} of the total {1:C2} they had. They spent {2:P2}.» -f 12.34, 45.67, (12.34/45.67)
They spent $12.34 of the total $45.67 they had. They spent 27.02 %.
PS C:\> «The number {0:N2} in exponential notation is {0:E6}» -f 123456.789
The number 123,456.79 in exponential notation is 1.234568E+005
Теперь, если совместить форматирование чисел с использованием выражений из предыдущей части поста, мы можем получить довольно мощную возможность кастомизации вывода.
PS C:\> Get-Volume | Sort DriveLetter | FT DriveType, DriveLetter, FileSystem, @{Expression={ «{0:N0}» -f ($.Size/1MB) }; Label=»Total (MB)»; Align=»Right» }, @{Expression={ «{0:N0}» -f (($.Size-$.SizeRemaining)/1MB) }; Label=»Used (MB)»; Align=»Right» }, @{Expression={ «{0:N0}» -f ($.SizeRemaining/1MB) }; Label=»Free (MB)»; Align=»Right» }, @{Expression={ «{0:P2}» -f ($.SizeRemaining/$.Size) }; Label=»Free %»; Align=»Right» } -AutoSize
DriveType DriveLetter FileSystem Total (MB) Used (MB) Free (MB) Free %
——— ———— ———- ———- ——— ——— ——
Fixed NTFS 350 287 63 18.09 %
Fixed C NTFS 243,845 213,652 30,193 12.38 %
Fixed D NTFS 476,937 56,928 420,009 88.06 %
Removable E NTFS 125,903 20,925 104,978 83.38 %
Fixed F NTFS 476,938 137,181 339,757 71.24 %
6. Объединение двух таблиц
Одна из полезных возможностей — это нахождение общих свойств нескольких объектов и создание единого запроса для вывода информации о каждом из них.
Например, вам может потребоваться вывести количество свободного места на диске, при выводе информации об общей папке. Или же вам нужно вывести список томов, с перечислением находящихся на них общих папок.
Обычно в таких случаях используется цикл для перебора значений одной из таблиц при помощи командлета ForEach (его алиас — %). Для ссылки на свойства текущего объекта в цикле используется $_.
В лбом случае, для нахождения правильных ассоциаций вам потребуются совпадающие либо связанные свойства из каждой таблицы. В реляционных базах данных это называется внешним ключом.
Так как вы работаете одновременно с двумя таблицами, возможно вам потребуется назначить значение внешнего объекта (содержащегося в $_) некоторой переменной, чтобы иметь возможность сравнения его с объектом внутренней таблицы (который тоже будет находиться в $_).
Пример, выводящий список всех томов и расположенных на них общих папок:
Get-Volume | Sort DriveLetter | % { $V=$_; $V | FT -AutoSize; Get-SmbShare | ? {$V.ObjectID –eq $_.Volume} | FT Name, Path -AutoSize }
Еще один пример, выводящий список общих папок со значением количества свободного места на диске, на котором они расположены.
Get-SmbShare | ? Special -ne $true | % { $_ | FT Name, Path -AutoSize; Get-Volume –ObjectID $_.Volume | FT DriveLetterSize, SizeRemaining}
PS C:\> Get-Volume | Sort DriveLetter | % { $V=$_; $V | FT -AutoSize; Get-SmbShare | ? {$V.ObjectID –eq $_.Volume} | FT Name, Path -AutoSize }
DriveLetter FileSystemLabel FileSystem DriveType HealthStatus SizeRemaining Size
———— ————— ———- ——— ———— ————- —-
System Reserved NTFS Fixed Healthy 63.3 MB 350 MB
DriveLetter FileSystemLabel FileSystem DriveType HealthStatus SizeRemaining Size
———— ————— ———- ——— ———— ————- —-
C SSD1 NTFS Fixed Healthy 29.49 GB 238.13 GB
Name Path
—- —-
ADMIN$ C:\windows
C$ C:\
print$ C:\windows\system32\spool\drivers
Projects C:\Projects
DriveLetter FileSystemLabel FileSystem DriveType HealthStatus SizeRemaining Size
———— ————— ———- ——— ———— ————- —-
D HDD NTFS Fixed Healthy 410.17 GB 465.76 GB
Name Path
—- —-
D$ D:\
Images D:\Images
DriveLetter FileSystemLabel FileSystem DriveType HealthStatus SizeRemaining Size
———— ————— ———- ——— ———— ————- —-
E SDCard NTFS Removable Healthy 102.52 GB 122.95 GB
Name Path
—- —-
BackupFiles E:\Backup
DriveLetter FileSystemLabel FileSystem DriveType HealthStatus SizeRemaining Size
———— ————— ———- ——— ———— ————- —-
F USB3 NTFS Fixed Healthy 331.79 GB 465.76 GB
Name Path
F$ F:\
PS C:\> Get-SmbShare | ? Special -ne $true | % { $_ | FT Name, Path -AutoSize; Get-Volume –ObjectID $_.Volume | FT DriveLetter, Size, SizeRemaining}
Name Path
—- —-
BackupFiles E:\Backup
DriveLetter Size SizeRemaining
———— —- ————-
E 132018860032 110077665280
Name Path
—- —-
Images D:\Images
DriveLetter Size SizeRemaining
———— —- ————-
D 500104687616 440411648000
Name Path
—- —-
print$ C:\windows\system32\spool\drivers
DriveLetter Size SizeRemaining
———— —- ————-
C 255690010624 31659585536
Name Path
—- —-
Projects C:\Projects
DriveLetter Size SizeRemaining
———— —- ————-
C 255690010624 31659585536
7. Создание пустых свойств объекта
Еще один интересный трюк — это создание объекта с несколькими пустыми свойствами, заполняются далее по мере выполнения скрипта. Для этого потребуется использование переменных, чтобы хранить временные результаты а также чуть более глубокой понимание таких структур, как циклы.
Эта возможность предоставляет нам более удобный способ объединения двух таблиц (что мы только что делали в предыдущей части поста), так как в итоге мы получаем один объект, содержащий все нужные нам свойства из обоих таблиц.
Например, результат командлета Get-SmbShare содержит свойство Volume, содержащее идентификатор тома, с помощью которого мы можем определить, на каком именно томе расположена данная общая папка. Но было бы лучше, если бы мы смогли получить некий объект, содержащий информацию о томе и общей папке, при помощи одного запроса.
Вот что мы можем сделать. Сначала нам нужно создать переменную с результатами команды Get-SmbShare, содержащую несколько свойств, которые мы заполним позже. Изначально они не будут содержать каких-либо значений. Далее мы воспользуемся циклом для перебора полученных значений, в котором заполним пустые свойства переменной результатами запроса свойств нужного тома на основании его идентификатора. После этого, результаты запроса можно вывести, используя любую из уже рассмотренных нами возможностей форматирования.
PS C:\> $Shares = Get-SmbShare | ? { $_.Special -ne $true} | Select Name, Path, Volume, Total, Used, Free
PS C:\> $Shares | FT -AutoSize
Name Path Volume Total Used Free
—- —- —— —— —- —-
BackupFiles E:\Backup \?\Volume{a68e6379-6763-11e3-8256-d89d67d108b9}\
Images D:\Images \?\Volume{4304337f-6763-11e3-8255-806e6f6e6963}\
print$ C:\windows\system32\spool\drivers \?\Volume{4304337d-6763-11e3-8255-806e6f6e6963}\
Projects C:\Projects \?\Volume{4304337d-6763-11e3-8255-806e6f6e6963}\
PS C:\> $Shares | % { $S=$_; $V=Get-Volume | ? ObjectId -eq $S.Volume; $S.Total=$V.Size/1MB; $S.Free=$V.SizeRemaining/1MB; $S.Used=($V.Size-$V.SizeRemaining)/1MB }
PS C:\> $Shares | Sort Name | FT Name, Path, @{Expression={ «{0:N0}» -f $.Total }; Label=»Total (MB)»; Align=»Right» }, @{Expression={ «{0:N0}» -f $.Used }; Label=»Used (MB)»; Align=»Right» }, @{Expression={ «{0:N0}» -f $.Free }; Label=»Free (MB)»; Align=»Right» }, @{Expression={ «{0:P2}» -f ($.Free/$_.Total) }; Label=»Free %»; Align=»Right» } -AutoSize
Name Path Total (MB) Used (MB) Free (MB) Free %
—- —- ———- ——— ——— ——
BackupFiles E:\Backup 125,903 20,925 104,978 83.38 %
Images D:\Images 476,937 56,928 420,009 88.06 %
print$ C:\windows\system32\spool\drivers 243,845 213,652 30,193 12.38 %
Projects C:\Projects 243,845 213,652 30,193 12.38 %
8. Создание объектов с пустыми свойствами
Используя похожий прием, вы можете создавать полностью пустые объекты, т.е. те, в которых ни одно свойство не содержит значения. Для этого можно просто передать пустое значение командлету Select и указать требуемые свойства.
PS C:\> $Alerts = «» | Select Severity, Entity, Instance, Description
Затем вы можете использовать любые средства для заполнения этих свойств и вывода полученных результатов.
PS C:\> Get-Volume | ? {($.SizeRemaining/$.Size) -lt 0.8} | % { $Alerts.Severity=»Warning»; $Alerts.Entity=»Volume»; $Alerts.Instance = $.DriveLetter+» «+$.ObjectID; $Alerts.Description=»Volume has less than 80% free space»; $Alerts} | FT -AutoSize
Severity Entity Instance Description
——— —— ——— ————
Warning Volume \?\Volume{4304337c-6763-11e3-8255-806e6f6e6963}\ Volume has less than 80% free space
Warning Volume F \?\Volume{c9337eca-6774-11e3-825a-b8763fd91487}\ Volume has less than 80% free space
Warning Volume C \?\Volume{4304337d-6763-11e3-8255-806e6f6e6963}\ Volume has less than 80% free space
Для повторного использования, возможно, стоит определить эту часть кода в качестве функции и дать ей имя, схожее по структуре с именем командлета.
Function Get-StorageAlert
{
$A = «» | Select Severity, Entity, Instance, Description
Get-Volume | ? DriveLetter | ? {($.SizeRemaining/$.Size) -lt 0.8} | % {
$A.Severity=»Warning»
$A.Entity=»Volume»
$A.Instance = $_.DriveLetter
$A.Description=»Volume has less than 80% free space»
$A
}
Get-SmbShare -ContinuouslyAvailable $false | ? {$.Special -ne $true} | % {
$A.Severity=»Warning»
$A.Entity=»Share»
$A.Instance = $.Name
$A.Description=»Share is not continuously available»
$A
}
}
Пример ее использования:
PS C:\> Get-StorageAlert | FT -AutoSize
Severity Entity Instance Description
——— —— ——— ————
Warning Volume F Volume has less than 80% free space
Warning Volume C Volume has less than 80% free space
Warning Share BackupFiles Share is not continuously available
Warning Share Images Share is not continuously available
Warning Share print$ Share is not continuously available
Warning Share Projects Share is not continuously available
Автор: Jose Barreto
Страницы в социальных сетях:
Twitter: https://twitter.com/vsseth
Facebook: https://fb.com/inpowershell
VKontakte: https://vk.com/inpowershell