Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Я хочу сделать шаг назад и говорить о хэштебли. Я использую их все время сейчас. Я рассказывал кому-то о них после нашей встречи с группой пользователей вчера вечером, и я понял, что у меня была та же путаница о них, как и у него. Хеш-таблицы очень важны в PowerShell, поэтому хорошо иметь чёткое понимание их работы.
Примечание.
Оригинальная версия этой статьи появилась в блоге, написанном @KevinMarquette. Команда PowerShell благодарит Кевина за предоставление этого содержимого нам. Пожалуйста, ознакомьтесь с его блогом на PowerShellExplained.com.
Хеш-таблица как коллекция вещей
Я хочу, чтобы вы сначала увидели хэш-таблицу как коллекцию в ее традиционном определении. Это определение дает вам базовое представление о том, как они работают, когда они используются для более сложных материалов позже. Пропуск этого понимания часто является источником путаницы.
Что такое массив?
Прежде чем приступить к объяснению, что такое хэш-таблица, я сначала должен упомянуть массивы. Для этого обсуждения массив представляет собой список или коллекцию значений или объектов.
$array = @(1,2,3,5,7,11)
После того как вы разместите элементы в массиве, можно использовать foreach
для прохода по списку или воспользоваться индексом для доступа к отдельным элементам в массиве.
foreach($item in $array)
{
Write-Output $item
}
Write-Output $array[3]
Можно также обновить значения с помощью индекса таким же образом.
$array[2] = 13
Я только начал разбираться с массивами, но это должно поставить их в правильный контекст, когда я перейду на хэш-таблицы.
Что такое хеш-таблица?
Я собираюсь начать с базового технического описания того, что такое хеш-таблицы в общем смысле, прежде чем перейти к другим способам их использования в PowerShell.
Хэш-таблицы — это структура данных, например массив, за исключением хранения каждого значения (объекта) с помощью ключа. Это простое хранилище ключей и значений. Сначала мы создадим пустую хеш-таблицу.
$ageList = @{}
Обратите внимание, что фигурные скобки, а не круглые скобки, используются для определения хеш-таблицы. Затем мы добавим элемент с помощью ключа следующим образом:
$key = 'Kevin'
$value = 36
$ageList.Add( $key, $value )
$ageList.Add( 'Alex', 9 )
Имя человека является ключом, и их возраст является значением, которое я хочу сохранить.
Использование квадратных скобок для доступа
После добавления значений в хэш-таблицы вы можете извлечь их обратно с помощью этого же ключа (вместо использования числового индекса, как и для массива).
$ageList['Kevin']
$ageList['Alex']
Когда я хочу узнать возраст Кевина, я использую его имя, чтобы получить к нему доступ. Этот подход можно использовать для добавления или обновления значений в хэш-таблицы. Это аналогично использованию приведенного выше метода Add()
.
$ageList = @{}
$key = 'Kevin'
$value = 36
$ageList[$key] = $value
$ageList['Alex'] = 9
Существует еще один синтаксис, который можно использовать для доступа и обновления значений, которые я рассмотрим в следующем разделе. Если вы перешли на PowerShell с другого языка, эти примеры должны соответствовать тому, как вы могли использовать хеш-таблицы раньше.
Создание хэш-таблиц со значениями
До сих пор я создал пустой хэш-файл для этих примеров. Вы можете предварительно заполнить ключи и значения при их создании.
$ageList = @{
Kevin = 36
Alex = 9
}
В качестве таблицы подстановки
Реальное значение этого типа хэш-таблицы заключается в том, что их можно использовать в качестве таблицы подстановки. Вот простой пример.
$environments = @{
Prod = 'SrvProd05'
QA = 'SrvQA02'
Dev = 'SrvDev12'
}
$server = $environments[$env]
В этом примере необходимо указать среду для переменной $env
и выбрать правильный сервер. Вы могли бы использовать switch($env){...}
для такого выбора, но хеш-таблица — хороший вариант.
Это становится еще лучше при динамической сборке таблицы подстановки, чтобы использовать ее позже. Поэтому думайте об использовании этого подхода, когда вам нужно перекрестно ссылаться на что-то. Я думаю, что мы увидели бы это еще больше, если бы PowerShell не был столь эффективен в фильтрации данных через конвейер с использованием Where-Object
. Если вы когда-либо находитесь в ситуации, когда производительность имеет значение, этот подход необходимо учитывать.
Я не скажу, что это быстрее, но это вписывается в правило : если производительность имеет значение, протестируйте его.
Множественный выбор
Как правило, вы считаете хэш-файл парой "ключ-значение", где вы предоставляете один ключ и получаете одно значение. PowerShell позволяет предоставить массив ключей для получения нескольких значений.
$environments[@('QA','DEV')]
$environments[('QA','DEV')]
$environments['QA','DEV']
В этом примере я использую такую же хэш-таблицу подстановки из вышеуказанного и три разных стиля массивов для получения совпадений. Это скрытая ценность в PowerShell, о которой большинство людей не знает.
Итерации хэш-таблиц
Так как хэш-лист представляет собой коллекцию пар "ключ-значение", вы выполняете итерацию по-другому, чем для массива или обычного списка элементов.
Первое, что следует заметить, что если вы отправляете хэш-файл, канал обрабатывает его как один объект.
PS> $ageList | Measure-Object
count : 1
Несмотря на то, что свойство Count
указывает, сколько значений он содержит.
PS> $ageList.Count
2
Эту проблему можно обойти с помощью свойства Values
, если все, что вам нужно, — это только значения.
PS> $ageList.Values | Measure-Object -Average
Count : 2
Average : 22.5
Часто рекомендуется перечислить ключи и использовать их для доступа к значениям.
PS> $ageList.Keys | ForEach-Object{
$message = '{0} is {1} years old!' -f $_, $ageList[$_]
Write-Output $message
}
Kevin is 36 years old
Alex is 9 years old
Ниже приведен тот же пример с циклом foreach(){...}
.
foreach($key in $ageList.Keys)
{
$message = '{0} is {1} years old' -f $key, $ageList[$key]
Write-Output $message
}
Мы перебираем каждый ключ в хеш-таблице и затем используем его для доступа к значению. Это распространенный шаблон при работе с хэш-файлами в качестве коллекции.
GetEnumerator()
Таким образом, мы переходим к GetEnumerator()
для итерации над нашей хеш-таблицей.
$ageList.GetEnumerator() | ForEach-Object{
$message = '{0} is {1} years old!' -f $_.Key, $_.Value
Write-Output $message
}
Перечислитель выдает каждую пару "ключ-значение" по очереди. Он был разработан специально для этого варианта использования. Спасибо Марк Краус за напоминание мне об этом.
Некорректное Перечисление
Одна из важных деталей заключается в том, что вы не можете изменить хэш-таблицу в процессе её перечисления. Если мы начнём с базового примера $environments
:
$environments = @{
Prod = 'SrvProd05'
QA = 'SrvQA02'
Dev = 'SrvDev12'
}
При попытке задать каждому ключу одно и то же значение сервера не удаётся.
$environments.Keys | ForEach-Object {
$environments[$_] = 'SrvDev03'
}
An error occurred while enumerating through a collection: Collection was modified;
enumeration operation may not execute.
+ CategoryInfo : InvalidOperation: tableEnumerator:HashtableEnumerator) [],
RuntimeException
+ FullyQualifiedErrorId : BadEnumeration
Это тоже потерпит неудачу, даже если похоже, что всё должно быть в порядке.
foreach($key in $environments.Keys) {
$environments[$key] = 'SrvDev03'
}
Collection was modified; enumeration operation may not execute.
+ CategoryInfo : OperationStopped: (:) [], InvalidOperationException
+ FullyQualifiedErrorId : System.InvalidOperationException
Перед выполнением перечисления необходимо клонировать ключи.
$environments.Keys.Clone() | ForEach-Object {
$environments[$_] = 'SrvDev03'
}
Хеш-таблица как коллекция свойств
До сих пор тип объектов, которые мы помещали в хеш-таблицу, был одинаковым. Я использовал возраст во всех этих примерах, и ключом было имя человека. Это отличный способ посмотреть на это, когда у каждого объекта в коллекции есть имя. Другой распространенный способ использования хеш-таблиц в PowerShell — это хранение коллекции свойств, где ключ является именем свойства. Я перейдю к этой идее в следующем примере.
Доступ на основе свойств
Использование доступа к свойствам изменяет динамику хэш-таблиц и их использование в PowerShell. Ниже приведен наш обычный пример выше, который рассматривает ключи как свойства.
$ageList = @{}
$ageList.Kevin = 35
$ageList.Alex = 9
Как и приведенные выше примеры, этот пример добавляет эти ключи, если они еще не существуют в хэш-файле. В зависимости от того, как вы определили ключи и каковы ваши значения, это либо слегка необычно, либо полностью соответствует. Пример списка возрастов работал отлично до этого момента. Нам нужен новый пример, чтобы чувствовать уверенность в будущем.
$person = @{
name = 'Kevin'
age = 36
}
И мы можем добавлять и получать доступ к атрибутам на $person
следующим образом.
$person.city = 'Austin'
$person.state = 'TX'
Внезапно эта хеш-таблица начинает вести себя как объект. Это по-прежнему коллекция вещей, поэтому все приведенные выше примеры по-прежнему применяются. Мы просто подходим к нему с другой точки зрения.
Проверка ключей и значений
В большинстве случаев можно просто проверить значение следующим образом:
if( $person.age ){...}
Это просто, но был источником многих ошибок для меня, потому что я упускал из виду одну важную детали в моей логике. Я начал использовать его, чтобы проверить, присутствует ли ключ. При значении $false
или ноль этот оператор неожиданно возвращает $false
.
if( $person.age -ne $null ){...}
Это решает проблему для нулевых значений, но не для $null и несуществующих ключей. Большую часть времени вам не нужно проводить это различие, но есть методы для тех случаев, когда это необходимо.
if( $person.ContainsKey('age') ){...}
У нас также есть ContainsValue()
для ситуации, когда необходимо проверить значение, не зная ключа или итерации всей коллекции.
Удаление и очистка ключей
Ключи можно удалить с помощью метода Remove()
.
$person.Remove('age')
Присвоение значения $null
просто приведёт к тому, что у вас останется ключ со значением $null
.
Распространенный способ очистить хеш-таблицу — просто инициализировать ее как пустую хеш-таблицу.
$person = @{}
Хотя это работает, попробуйте использовать вместо этого метод Clear()
.
$person.Clear()
Это один из тех случаев, когда использование метода создает самодокументируемый код, делая намерения кода очевидными.
Все веселые вещи
Упорядоченные хеш-таблицы
По умолчанию хэш-файлы не упорядочены (или отсортированы). В традиционном контексте порядок не имеет значения при использовании ключа для доступа к значениям. Вы можете обнаружить, что захотите, чтобы свойства оставались в порядке, который вы определили. К счастью, есть способ сделать это с помощью ключевого слова ordered
.
$person = [ordered]@{
name = 'Kevin'
age = 36
}
Теперь при перечислении ключей и значений они остаются в этом порядке.
Встроенные хеш-таблицы
При определении хеш-таблицы в одной строке можно разделить пары "ключ-значение" точкой с запятой.
$person = @{ name = 'kevin'; age = 36; }
Это будет удобно, если вы создаете их на трубе.
Пользовательские выражения в стандартных командах конвейера
Существует несколько командлетов, поддерживающих использование хеш-таблиц для создания пользовательских или вычисляемых свойств. Вы часто видите это с Select-Object
и Format-Table
. Хеш-таблицы имеют специальный синтаксис, который выглядит следующим образом при полном развертывании.
$property = @{
Name = 'TotalSpaceGB'
Expression = { ($_.Used + $_.Free) / 1GB }
}
Name
— это то, как командлет обозначает тот столбец.
Expression
— это блок скрипта, который выполняется, где $_
является значением объекта в канале. Ниже приведен сценарий в действии:
$drives = Get-PSDrive | where Used
$drives | Select-Object -Property Name, $property
Name TotalSpaceGB
---- ------------
C 238.472652435303
Я поместил это в переменную, но можно легко сделать это встроенно, и вы можете изменить Name
на n
и Expression
на e
по ходу дела.
$drives | Select-Object -Property Name, @{n='TotalSpaceGB';e={($_.Used + $_.Free) / 1GB}}
Мне лично не нравится, насколько длинными это делает команды, и это часто способствует некоторым негативным привычкам, которые я не буду обсуждать. Вероятнее всего, я создам новую хэш-таблицу или pscustomobject
с нужными мне полями и свойствами вместо использования этого подхода в сценариях. Есть много кода, который это делает, поэтому я хотел, чтобы вы знали об этом. Позже я говорю о создании pscustomobject
.
Настраиваемое выражение сортировки
Можно легко сортировать коллекцию, если у объектов есть данные, по которым требуется отсортировать. Вы можете добавить данные в объект перед сортировкой или создать пользовательское выражение для Sort-Object
.
Get-ADUser | Sort-Object -Property @{ e={ Get-TotalSales $_.Name } }
В этом примере я беру список пользователей и использую настраиваемый командлет, чтобы получить дополнительную информацию исключительно для сортировки.
Сортировка списка хэш-таблиц
Если у вас есть список хэш-таблиц, которые вы хотите сортировать, обратите внимание, что Sort-Object
не рассматривает ключи как свойства. Мы можем обойти это, используя пользовательское выражение сортировки.
$data = @(
@{name='a'}
@{name='c'}
@{name='e'}
@{name='f'}
@{name='d'}
@{name='b'}
)
$data | Sort-Object -Property @{e={$_.name}}
Распаковка хеш-таблиц в командлетах
Одно из моих любимых достоинств хеш-таблиц — это то, что многие люди не узнают о них сразу. Идея заключается в том, что вместо того, чтобы указывать все свойства командлету в одной строке, можно сначала упаковать их в хэш-таблицу. Затем вы можете передать хеш-таблицу функции особым способом. Ниже приведен пример создания области DHCP обычным способом.
Add-DhcpServerV4Scope -Name 'TestNetwork' -StartRange '10.0.0.2' -EndRange '10.0.0.254' -SubnetMask '255.255.255.0' -Description 'Network for testlab A' -LeaseDuration (New-TimeSpan -Days 8) -Type "Both"
Без использования расстановки , все эти вещи необходимо определить в одной строке. Он либо прокручивается за экран, либо переносится туда, где захочет. Теперь сравните это с командой, которая использует технику "splatting".
$DHCPScope = @{
Name = 'TestNetwork'
StartRange = '10.0.0.2'
EndRange = '10.0.0.254'
SubnetMask = '255.255.255.0'
Description = 'Network for testlab A'
LeaseDuration = (New-TimeSpan -Days 8)
Type = "Both"
}
Add-DhcpServerV4Scope @DHCPScope
Использование знака @
вместо $
вызывает операцию splat.
Просто уделите минуту, чтобы оценить, насколько легко этот пример читается. Они представляют собой совершенно одинаковые команды с одинаковыми значениями. Второй проще понять и поддерживать в дальнейшем.
Я использую развёртывание аргументов, когда команда становится слишком длинной. Я считаю "слишком долго" это когда окно прокручивается вправо. Если я использую три свойства для функции, вероятно, что я перезапишу её с помощью разложенной хеш-таблицы.
Сплаттинг для необязательных параметров
Один из наиболее распространенных способов, которым я использую splatting, заключается в том, чтобы иметь дело с необязательными параметрами, поступающими из другого места в моем сценарии. Предположим, у меня есть функция, которая обёртывает вызов Get-CimInstance
с необязательным аргументом $Credential
.
$CIMParams = @{
ClassName = 'Win32_BIOS'
ComputerName = $ComputerName
}
if($Credential)
{
$CIMParams.Credential = $Credential
}
Get-CimInstance @CIMParams
Сначала создаю хэш-файл с общими параметрами. Затем я добавлю $Credential
, если он существует.
Так как я использую сплаттинг здесь, мне нужно только вызвать Get-CimInstance
в моем коде один раз. Этот шаблон конструктора очень чист и может легко обрабатывать множество необязательных параметров.
По справедливости, вы могли бы написать команды, которые разрешают значения $null
для параметров. Вы просто не всегда контролируете другие команды, которые вы вызываете.
Несколько брызг
Можно добавить несколько хэш-элементов в один и тот же командлет. Если мы вернемся к нашему исходному примеру с платтингом:
$Common = @{
SubnetMask = '255.255.255.0'
LeaseDuration = (New-TimeSpan -Days 8)
Type = "Both"
}
$DHCPScope = @{
Name = 'TestNetwork'
StartRange = '10.0.0.2'
EndRange = '10.0.0.254'
Description = 'Network for testlab A'
}
Add-DhcpServerv4Scope @DHCPScope @Common
Я буду использовать этот метод, если у меня есть общий набор параметров, которые я передаваю многим командам.
Техника splatting для чистого кода
Нет ничего плохого при сложении одного параметра, если вы очищаете код.
$log = @{Path = '.\logfile.log'}
Add-Content "logging this command" @log
Сплаттинг исполняемых файлов
Splatting также работает на некоторых исполняемых файлах, которые используют синтаксис /param:value
.
Robocopy.exe
, например, имеет некоторые параметры, такие как это.
$robo = @{R=1;W=1;MT=8}
robocopy source destination @robo
Я не знаю, насколько это полезно, но мне показалось это интересным.
Добавление хеш-таблиц
хеш-таблицы поддерживают оператор сложения для объединения двух хеш-таблиц.
$person += @{Zip = '78701'}
Это работает только в том случае, если два хэш-файла не используют ключ.
Вложенные хэш-таблицы
Хэш-таблицы можно использовать в качестве значений внутри хэш-таблицы.
$person = @{
name = 'Kevin'
age = 36
}
$person.location = @{}
$person.location.city = 'Austin'
$person.location.state = 'TX'
Я начал с базового хэш-файла, содержащего два ключа. Я добавил ключ, названный location
, с пустой хеш-таблицей. Затем я добавил последние два элемента в этот хэш-файл location
. Мы можем сделать это все прямо здесь.
$person = @{
name = 'Kevin'
age = 36
location = @{
city = 'Austin'
state = 'TX'
}
}
Это создает ту же хеш-таблицу, которую мы видели выше, и может получить доступ к свойствам таким же образом.
$person.location.city
Austin
Существует множество способов подхода к структуре объектов. Вот второй способ посмотреть на вложенную хеш-таблицу.
$people = @{
Kevin = @{
age = 36
city = 'Austin'
}
Alex = @{
age = 9
city = 'Austin'
}
}
Это смешивает концепцию использования хэш-таблиц как коллекции объектов и коллекции свойств. Значения по-прежнему легко доступны, даже если они вложены, вне зависимости от того, какой подход вы предпочитаете.
PS> $people.kevin.age
36
PS> $people.kevin['city']
Austin
PS> $people['Alex'].age
9
PS> $people['Alex']['City']
Austin
Как правило, я использую свойство dot, когда я рассматриваю его как свойство. Это, как правило, вещи, которые я определил статически в моем коде, и я знаю их по памяти. Если мне нужно ходить по списку или программно обращаться к ключам, я использую квадратные скобки, чтобы указать имя ключа.
foreach($name in $people.Keys)
{
$person = $people[$name]
'{0}, age {1}, is in {2}' -f $name, $person.age, $person.city
}
Наличие возможности вложить хэш-диаграммы дает вам много гибкости и параметров.
Рассмотрение вложенных хеш-таблиц
Как только вы начнете использовать вложенные хэш-таблицы, вам потребуется простой способ просматривать их через консоль. Если я беру эту последнюю хэш-таблицу, я получаю результат, который выглядит следующим образом и уходит на определенную глубину:
PS> $people
Name Value
---- -----
Kevin {age, city}
Alex {age, city}
Моя основная команда для просмотра этих вещей — это ConvertTo-Json
, потому что она очень понятная, и я часто использую JSON для других задач.
PS> $people | ConvertTo-Json
{
"Kevin": {
"age": 36,
"city": "Austin"
},
"Alex": {
"age": 9,
"city": "Austin"
}
}
Даже если вы не знаете JSON, вы должны иметь возможность видеть то, что вы ищете. Существует команда Format-Custom
для структурированных данных, как это, но мне все еще нравится представление JSON.
Создание объектов
Иногда просто нужно иметь объект, и использование хеш-таблицы для хранения свойств просто не дает нужного результата. Чаще всего вы хотите видеть ключи в качестве имен столбцов.
pscustomobject
делает это легко.
$person = [pscustomobject]@{
name = 'Kevin'
age = 36
}
$person
name age
---- ---
Kevin 36
Даже если вы изначально не создаёте его как pscustomobject
, вы всегда можете привести его позже по необходимости.
$person = @{
name = 'Kevin'
age = 36
}
[pscustomobject]$person
name age
---- ---
Kevin 36
У меня уже есть подробная запись для pscustomobject, которую вам стоит прочитать после этого сообщения. Он основывается на многих знаниях и навыках, изученных здесь.
Чтение и запись хэш-таблиц в файл
Сохранение в CSV
Борьба с получением хэш-таблицы при сохранении его в CSV — это одна из трудностей, о которых я упомянул выше. Преобразуйте вашу хэш-таблицу в pscustomobject
и корректно сохраните её в CSV. Полезно начинать с pscustomobject
, чтобы был сохранён порядок столбцов. Но при необходимости вы можете привести его к встроенному pscustomobject
.
$person | ForEach-Object{ [pscustomobject]$_ } | Export-Csv -Path $path
Опять же, ознакомьтесь с моей статьей о применении pscustomobject.
Сохранение вложенной хэш-таблицы в файл
Если мне нужно сохранить вложенную хэш-таблицу в файл, а затем снова прочитать её, я использую командлеты JSON для этого.
$people | ConvertTo-Json | Set-Content -Path $path
$people = Get-Content -Path $path -Raw | ConvertFrom-Json
Существует два важных пункта об этом методе. Во-первых, json записывается в многострочные строки, поэтому мне нужно использовать параметр -Raw
для чтения его обратно в одну строку. Во-вторых, импортированный объект больше не является [hashtable]
. Теперь это [pscustomobject]
и это может вызвать проблемы, если вы этого не ожидаете.
Следите за глубоко вложенными хэш-файлами. При преобразовании в JSON вы можете не получить ожидаемые результаты.
@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json
{
"a": {
"b": {
"c": "System.Collections.Hashtable"
}
}
}
Убедитесь, что вы использовали параметр глубины для расширения всех вложенных хэш-таблиц.
@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json -Depth 3
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
Если нужно, чтобы при импорте было [hashtable]
, вам нужно использовать команды Export-CliXml
и Import-CliXml
.
Преобразование JSON в Hashtable
Если вам нужно преобразовать JSON в [hashtable]
, есть один способ, который я знаю, чтобы сделать это с JavaScriptSerializer в .NET.
[Reflection.Assembly]::LoadWithPartialName("System.Web.Script.Serialization")
$JSSerializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
$JSSerializer.Deserialize($json,'Hashtable')
Начиная с PowerShell версии 6 поддержка JSON использует JSON.NET NewtonSoft и добавляет хэш-поддержку.
'{ "a": "b" }' | ConvertFrom-Json -AsHashtable
Name Value
---- -----
a b
PowerShell 6.2 добавил параметр глубины для ConvertFrom-Json
. Значение глубина по умолчанию — 1024.
Чтение непосредственно из файла
Если у вас есть файл, содержащий хэш-таблицы с помощью синтаксиса PowerShell, можно импортировать его напрямую.
$content = Get-Content -Path $Path -Raw -ErrorAction Stop
$scriptBlock = [scriptblock]::Create( $content )
$scriptBlock.CheckRestrictedLanguage( $allowedCommands, $allowedVariables, $true )
$hashtable = ( & $scriptBlock )
Он импортирует содержимое файла в scriptblock
, а затем проверяет наличие других команд PowerShell перед его выполнением.
При этом вы знаете, что манифест модуля (файл .psd1
) является просто хэш-таблицой?
Ключи могут быть любым объектом
В большинстве случаев ключи являются просто строками. Так что мы можем поместить цитаты вокруг чего-либо и сделать его ключом.
$person = @{
'full name' = 'Kevin Marquette'
'#' = 3978
}
$person['full name']
Вы можете сделать некоторые странные вещи, которые вы, возможно, не осознавали.
$person.'full name'
$key = 'full name'
$person.$key
Просто потому, что вы можете сделать что-то, это не означает, что вы должны. Последний скорее всего вызовет проблему и будет легко неправильно понят каждым, кто прочитает ваш код.
Технически ваш ключ не обязательно должен быть строкой, но о них проще думать, если использовать только строки. Однако индексирование не работает хорошо со сложными ключами.
$ht = @{ @(1,2,3) = "a" }
$ht
Name Value
---- -----
{1, 2, 3} a
Доступ к значению в хэш-файле по ключу не всегда работает. Рассмотрим пример.
$key = $ht.Keys[0]
$ht.$($key)
a
$ht[$key]
a
Если ключ является массивом, необходимо обернуть переменную $key
в подвыражение, чтобы ее можно было использовать с нотацией доступа к члену (.
). Кроме того, можно использовать нотацию индекса массива ([]
).
Использование в автоматических переменных
$PSBoundParameters
$PSBoundParameters — это автоматическая переменная, которая существует только в контексте функции. Он содержит все параметры, с которыми была вызвана функция. Это не совсем хэш-код, но достаточно близко, что вы можете относиться к нему как один.
Это включает удаление ключей и их передачу в другие функции. Если вы обнаруживаете, что пишете прокси-функции, обратите внимание на эту.
Дополнительные сведения см. в about_Automatic_Variables.
PSBoundParameters ловушка
Важно помнить, что это включает только значения, передаваемые в качестве параметров. Если у вас есть параметры со значениями по умолчанию, но они не передаются вызывающим кодом, $PSBoundParameters
не содержит эти значения. Обычно это не учитывается.
$PSDefaultParameterValues
Эта переменная автоматизации позволяет назначать значения по умолчанию этому командлету без изменения командлета. Ознакомьтесь с этим примером.
$PSDefaultParameterValues["Out-File:Encoding"] = "UTF8"
Это добавляет запись в хэш-файл $PSDefaultParameterValues
, который задает UTF8
в качестве значения по умолчанию для параметра Out-File -Encoding
. Это зависит от сеанса, поэтому его следует поместить в $PROFILE
.
Это часто используется для предварительного назначения значений, которые я вводю довольно часто.
$PSDefaultParameterValues[ "Connect-VIServer:Server" ] = 'VCENTER01.contoso.local'
Это также поддерживает использование подстановочных знаков, чтобы вы могли задавать значения пакетно. Ниже приведены некоторые способы использования:
$PSDefaultParameterValues[ "Get-*:Verbose" ] = $true
$PSDefaultParameterValues[ "*:Credential" ] = Get-Credential
Более подробную разбивку см. в этой отличной статье по автоматические значения по умолчаниюМайкл Соренс.
Regex $Matches
При использовании оператора -match
создается автоматическая переменная с именем $Matches
с результатами сопоставления. Если у вас есть какие-либо подвыражения в регулярном выражении, эти подсовпадения также перечислены.
$message = 'My SSN is 123-45-6789.'
$message -match 'My SSN is (.+)\.'
$Matches[0]
$Matches[1]
Именованные совпадения
Это одна из моих любимых функций, о которые большинство людей не знают. Если вы используете именованное совпадение регулярных выражений, вы можете получить доступ к этим совпадениям по имени в совпадениях.
$message = 'My Name is Kevin and my SSN is 123-45-6789.'
if($message -match 'My Name is (?<Name>.+) and my SSN is (?<SSN>.+)\.')
{
$Matches.Name
$Matches.SSN
}
В приведенном выше примере (?<Name>.*)
является именованным подвыражением. Затем это значение помещается в свойство $Matches.Name
.
Group-Object -AsHashtable
Одна из немногих известных функций Group-Object
заключается в том, что он может превратить некоторые наборы данных в хэш-файл для вас.
Import-Csv $Path | Group-Object -AsHashtable -Property Email
Это добавит каждую строку в хэш-файл и использует указанное свойство в качестве ключа для доступа к нему.
Копирование хэштеблей
Важно знать, что хэш-объекты являются объектами. И каждая переменная является просто ссылкой на объект. Это означает, что для создания допустимой копии хэш-файла требуется больше работы.
Назначение ссылочных типов
Когда у вас есть одна хеш-таблица и вы назначаете её второй переменной, обе переменные указывают на одну и ту же хеш-таблицу.
PS> $orig = @{name='orig'}
PS> $copy = $orig
PS> $copy.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.name
PS> 'Orig: [{0}]' -f $orig.name
Copy: [copy]
Orig: [copy]
Это подчеркивает, что они одинаковы, так как изменение значений в одном также изменит значения в другом. Это также применяется при передаче хэш-таблиц в другие функции. Если эти функции вносят изменения в этот хэш-файл, исходный файл также изменяется.
Мелкие копии, один уровень
Если у нас есть простая хэш-таблица, как в приведенном выше примере, мы можем использовать Clone()
для создания поверхностной копии.
PS> $orig = @{name='orig'}
PS> $copy = $orig.Clone()
PS> $copy.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.name
PS> 'Orig: [{0}]' -f $orig.name
Copy: [copy]
Orig: [orig]
Это позволит нам внести некоторые основные изменения в один из них, которые не повлияют на другой.
Мелкие копии, вложенные
Причина, по которой она называется неглубокой копией, заключается в том, что она копирует только свойства базового уровня. Если одно из этих свойств является ссылочным типом (например, другим хэш-файлом), эти вложенные объекты по-прежнему указывают друг на друга.
PS> $orig = @{
person=@{
name='orig'
}
}
PS> $copy = $orig.Clone()
PS> $copy.person.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.person.name
PS> 'Orig: [{0}]' -f $orig.person.name
Copy: [copy]
Orig: [copy]
Таким образом, вы можете увидеть, что, хотя я клонировал хэш-файл, ссылка на person
не была клонирована. Нам нужно сделать глубокую копию, чтобы действительно получить вторую хэш-таблицу, которая не связана с первой.
Глубокие копии
Существует несколько способов сделать глубокую копию хеш-таблицы (и сохранить её как хеш-таблицу). Ниже приведена функция с помощью PowerShell для рекурсивного создания глубокой копии:
function Get-DeepClone
{
[CmdletBinding()]
param(
$InputObject
)
process
{
if($InputObject -is [hashtable]) {
$clone = @{}
foreach($key in $InputObject.Keys)
{
$clone[$key] = Get-DeepClone $InputObject[$key]
}
return $clone
} else {
return $InputObject
}
}
}
Он не обрабатывает другие ссылочные типы или массивы, но это хорошая отправная точка.
Другой способ — использовать .NET для десериализации с помощью CliXml, как в этой функции:
function Get-DeepClone
{
param(
$InputObject
)
$TempCliXmlString = [System.Management.Automation.PSSerializer]::Serialize($obj, [int32]::MaxValue)
return [System.Management.Automation.PSSerializer]::Deserialize($TempCliXmlString)
}
Для очень больших хеш-таблиц функция десериализации работает быстрее по мере увеличения их размера. Однако при использовании этого метода следует учитывать некоторые аспекты. Так как он использует CliXml, это потребляет много памяти, и если вы клонируете огромные хэш-таблицы, это может быть проблемой. Еще одним ограничением cliXml является то, что существует ограничение глубины 48. Это означает, что если у вас есть хеш-таблица с 48 слоями вложенных хеш-таблиц, клонирование завершится ошибкой, и никакая хеш-таблица не будет выведена.
Что-нибудь ещё?
Я быстро многое успела. Моя надежда заключается в том, что каждый раз, когда вы читаете это, вы усваиваете что-то новое или лучше понимаете его. Так как я рассмотрел полный спектр этой функции, есть аспекты, которые просто не могут применяться к вам прямо сейчас. Это идеально нормально и является ожидаемым в зависимости от того, сколько вы работаете с PowerShell.
PowerShell