Глава 10. Конвейер, глубже
Содержание
- Глава 10. Конвейер, глубже
- Конвейер: включение мощности при меньшем наборе
- Как PowerShell передаёт данные вниз по конвейеру
- План A: Ввод конвейера по значению
- План B: Ввод конвейера по имени свойства
- Когда вещи не выстраиваются в линию: персонализация свойств
- Работа с Azure PowerShell
- Заключённые в скобки команды
- Выделение значения из отдельного свойства
- Лабораторные занятия
- Ответы лабораторных занятий
- Дальнейшее исследование
на данный момент вы научились достаточно действенно работать с конвейером PowerShell. Запуск команд (скажем,
Get-Process | Sort-Object VM -desc | ConvertTo-Html | Out-File procs.html
) это мощное
средство, исполняющее в одной строке то, что ранее занимало несколько строк сценария. Но вы можете достичь большего. В данной
главе мы глубже окунёмся в конвейер и раскроем некоторые из его самых мощных возможностей, которые позволяют нам верно
передавать данные между командами с меньшими усилиями.
Одна из причин по которой мы настолько любим PowerShell состоит в том, что он позволяет нам быть более действенными при администрировании без написания сложных сценариев, чем нам приходится пользоваться в Bash. Основной ключ к мощным командам в одну строку лежит в том как работает конвейер PowerShell.
Давайте внесём ясность: вы можете пропустить эту главу и по- прежнему действенно применять PowerShell, но в большинстве случаев вам придётся прибегать к сценариям и программам в стиле Bash. Хотя возможности конвейера PowerShell и могут быть сложными их, вероятно, легче освоить, нежели более сложные навыки программирования. Научившись обращаться с конвейером, вы сможете работать намного более эффективно и при этом не прибегать к сценариям.
Основная идея здесь целиком состоит в том, чтобы заставить саму оболочку выполнять для вас больше работы, причём при как можно меньшем наборе текста. Мы полагаем, что вы будете удивлены тем, насколько хорошо сама оболочка способна осуществлять это!
Всякий раз, когда вы связываете вместе две команды PowerShell должен выяснить как преобразовать вывод первой команды во ввод второй команды. В идущих следом примерах мы будем называть первую команду Командой A. Это производящая нечто команда. Вторая команда это Команда B, которой требуется принять вывод Команды A и затем выполнить свои собственные вещи:
CommandA | CommandB
К примеру, допустим что у вас имеется текстовый файл, который содержит по одному названию модуля в каждой строке, как это показано на Рисунке 10.1.
Рисунок 10-1
Создание в VS Code текстового файла, содержащего названия модулей, по одному названию в строке
Вы можете пожелать воспользоваться этими названиями модулей в качестве входных данных для команды, сообщая такой команде для какого модуля вы хотите исполнять её. Рассмотрим такой пример:
Get-Content .\modules.txt | Get-Command
Когда исполняется Get-Content
, она помещает названия модулей в свой конвейер.
Затем PowerShell принимает решение как получить это в команде Get-Command
. Основная
хитрость с PowerShell состоит в том, что такая команда способна принимать входные данные исключительно через параметры.
PowerShell должен решить какой параметр Get-Command
примет вывод
Get-Content
. Такой процесс определения носит название
связывания параметра конвейера
Get-Content
в неком
параметре Get-Command
. Первый метод который испробует его оболочка носит название
ByValue
(по значению); если он не сработает, он испытает
ByPropertyName
.При помощи этого метода связывания параметра конвейера PowerShell просматривает тип производимого Командой A объекта и
пытается определить будет ли параметр Команды B принимать такой тип объекта из своего конвейера. Вы можете определить это
самостоятельно: Сначала направьте вывод Команды A в Get-Member
чтобы увидеть какой
тип производит Команда A. Затем изучите полную подсказку Команды B (то есть Get-Help Get-Command
-Full
) чтобы увидеть будут ли какие- то параметры принимать этот тип данных из своего конвейера
ByValue
. Рисунок 10.2 отображает что вы можете выявить.
Что вы обнаружите, так это то что Get-Content
производит объекты с типом
System.String
(или для краткости String
). Вы
Get-Command
не обладает параметром, который принимает
String
из своего конвейера ByValue
. Основная
проблема состоит в том, что имеется параметр -Name
, который согласно имеющейся
подсказки "определяет массив названий. Данный командлет получает только команды, которые обладают указанным названием."
Это вовсе не то что нам нужно - наш текстовый файл, а тем более наши объекты String
,
это названия модулей, а не названия команд. Если мы исполним
Get-Content .\modules.txt | Get-Command
попробует выполнить выборку команд с названиями Microsoft.PowerShell.Archive
и так
далее, что, скорее всего, не сработает.
Когда из своего конвейера множество параметров принимают один и тот же тип, все параметры получат одно и то же значение.
Поскольку параметр -Name
из своего конвейера ByValue
принимает String
, для любых практических целей никакие иные параметры не способны
выполнять это. Что разбивает наши надежды на попытку передать названия модулей из своего текстового файла в
Get-Command
.
В данном случае вход конвейера работает, однако не достигает необходимых результатов, на которые мы надеялись. Давайте рассмотрим другой пример, в котором мы достигаем желаемого результата. Вот необходимая командная строка:
Get-Content ./modules.txt | Get-Module
Давайте направим получаемый из Команды A вывод в Get-Member
и изучим полную подсказку
для Команды B. Рисунок 10.3 показывает что вы обнаружите.
Get-Content
производит объекты с типомString
Get-Module
способен принимать такие объекты string
из своего конвейера ByValue
; это он и выполняет для своего параметра
-Name
. Согласно своей подсказки, данный параметр "определяет названия и шаблоны
имён модулей, которые получает данный командлет." Иными словами, Команда A получает один или более объектов
String
, а Команда B пытается обнаружить модуль со значением названия в данной строке.
Совет | |
---|---|
По большей части, использующие одно и то же существительное команды (как |
Давайте рассмотрим ещё один пример:
Get-ChildItem -File | Stop-Process -WhatIf
С первого взгляда может показаться что в этом нет никакого смысла. Но давайте проверим это, направив вывод Команды A в
Get-Member
и повторно рассмотрев подсказку для Команды B. На Рисунке 10.4
показано что вы должны обнаружить.
Get-ChildItem
производит объекты с типом FileInfo
(технически говоря, System.IO.FileInfo
, но вы обычно можете применять последний
фрагмент TypeName
в качестве сокращения). К сожалению, нет отдельного параметра
Stop-Process
, который способен принимать объект FileInfo
.
Наш подход ByValue
завершается неудачей и PowerShell предпримет попытку своего резервного
плана: ByPropertyName
.
При таком подходе вы всё ещё рассматриваете подключение получаемого из Команды A вывода к параметрам Команды B. Однако
ByPropertyName
слегка отличается от ByValue
.
При этом резервном методе имеется возможность вовлекать большое число параметров Команды B. И снова, направьте вывод
Команды A в Get-Member
, а затем просмотрите синтаксис для Команды B. Рисунок 10.5
отображает что вы должны обнаружить: вывод Команды A обладает одним свойством, чьё название соответствует некому параметру
Команды B.
Многие парни впадают в ступор от того что здесь происходит, а потому давайте проясним насколько тривиальна сама оболочка:
она ищет названия свойств, которые соответствуют именам параметров. И ничего более. Поскольку само свойство
Name
в результате синтаксического анализа даёт тот же самый результат что и наш параметр
-Name
, наша оболочка пытается соединить их.
Однако она не может сделать это прямо сейчас; сначала её необходимо убедиться что это параметр
-Name
примет ввод из своего конвейера ByPropertyName
.
Чтобы выполнить такое определение бросим взгляд на полную подсказку, которая показана на Рисунке 10.6.
Рисунок 10-6
Проверка того, будет ли параметр
-Name
команды Stop-Process
принимать вод конвейера ByPropertyName
В данном случае -Name
на самом деле принимает ввод из конвейера
ByPropertyName
, а потому это подключение сработает. Теперь вот в чём наша хитрость:
в отличии от ByValue
, который вовлекает лишь один параметр,
ByPropertyName
соединяет все совпадающие свойства и параметры (что предоставляет
возможность каждому разработанному параметру принимать вывод конвейера ByPropertyName
).
В текущем примере мы имеем совпадение только Name
и
-Name
. И что в результате Изучите Рисунок 10.7.
Мы наблюдаем кучу сообщений об ошибках. Основная проблема состоит в том, что названия файлов обычно такие вещи как
chapter7.zip и computers.txt, в то время как исполняемые объекты должны выглядеть как
pwsh
. Stop-Process
имеет дело лишь с такими
исполняемыми названиями. Однако даже хотя наше свойство Name
соединено через
имеющийся конвейер с параметром -Name
, все значения внутри свойства
Name
н существенны для параметра -Name
, что
и влечёт за собой ошибки.
Давайте взглянем на более удачный пример. Создадим в Visual Studio Code простой файл CSV, воспользовавшись примером с Рисунка 10.8.
Сохраните это файл как aliases.txt. Теперь вернитесь в свою оболочку, попробуйте его импортировать, как это показано на Рисунке
10.9. Вам также следует отправить получаемый в Import-Csv
вывод в
Get-Member
с тем, чтобы вы могли изучить имеющихся участников вывода.
Вы отчётливо можете наблюдать, что имеющиеся в файле CSV столбцы стали свойствами, а каждая строка данных в этом файле CSV
превратилась в некий объект. Теперь изучим имеющуюся подсказку для New-Alias
, как это
показано на Рисунке 10.10.
Оба свойства (Name
и Value
) соответствуют
именам в New-Alias
. Очевидно, это было осуществлено намеренно - при создании CSV файла
вы можете называть эти столбцы как угодно. Теперь проверьте принимают ли -Name
и
-Value
ввод конвейера ByPropertyName
,
как это отражено на Рисунке 10.11.
Рисунок 10-11
Проверка того, что
Name
и Value
являются параметрами, которые принимают входные данные ByPropertyName
Да, это делают оба параметра, что означает что наша хитрость сработала. Попробуем выполнить свою команду
Import-Csv .\aliases.txt | New-Alias
Получаемым результатом являются три новых псевдонима с названиями d
,
sel
и go
, которые указывают, соответственно, на
команды Get-ChildItem
, Select-Object
и
Invoke-Command
. Это мощная технология передачи данных из одной команды в другую, а также
для выполнения сложных задач с минимальным числом команд.
Наш пример с CSV это круто, но когда вы создаёте свои входные данные с нуля достаточно просто выровнять имена свойств и параметров. Когда вы вынуждены иметь дело с созданными для вас объектами или с созданными кем- то иным данными, ситуация усложняется.
Для своего следующего примера давайте поиграем с новой командой: New-ADUser
. Она
является частью модуля Active Directory. Вы можете получить этот модуль в компьютере клиента, установив Microsoft RSAT
(Remote Server Administration Tools, Инструменты удалённого администрирования серверов). Однако на этот раз не беспокойтесь
о выполнении этой команды: следуйте за нашим примером.
New-ADUser
обладает параметрами, разработанным для принятия сведений о новом
пользователе Active Directory. Вот некие образцы:
-
-Name
(обязательный) -
-samAccountName
(технически говоря, не обязательный, однако вам следует предоставлять его чтобы превращать данную учётную запись в используемую) -
-Department
-
-City
-
-Title
Мы можем показать и прочие, но давайте поработаем с этими. Все они принимают ввод из конвейера
ByPropertyName
.
Для этого примера вы снова предполагаете что у вас имеется файл CSV, однако он поступает из подразделения трудовых ресурсов или персонала. Вы десятки раз указывали им желаемый формат файла, однако они упорно давали нечто похожее, но не совсем верное, как это отражено на Рисунке 10.12.
Как вы можете наблюдать на Рисунке 10.12, ваша оболочка способна прекрасно импортировать этот файл CSV, получая в результате
три объекта с четырьмя свойствами для каждого. Основная проблема состоит в том, что свойство dept
не выравнивается с имеющимся параметром -Department
для
New-ADUser
, свойство login
бессмысленно, и у вас
нет samAccountName
или Name
- тех двух, которые
необходимы если вы желаете иметь возможность запустить эту команду чтобы создать новых пользователей:
PS C:\> import-csv .\newusers.csv | new-aduser
Как мы можем исправить это? Вы можете открыть данный файл CSV и исправить его, однако это означает много работы вручную с большими затратами времени, а вся цель PowerShell состоит в снижении ручного труда. Почему бы вместо этого не настроить нашу оболочку на исправление этого? Рассмотрим следующий пример:
PS C:\> import-csv .\newusers.csv |
>> select-object -property *,
>> @{name='samAccountName';expression={$_.login}},
>> @{label='Name';expression={$_.login}},
>> @{n='Department';e={$_.Dept}}
>>
login : TylerL
dept : IT
city : Seattle
title : IT Engineer
samAccountName : TylerL
Name : TylerL
Department : IT
login : JamesP
dept : IT
city : Chattanooga
title : CTO
samAccountName : JamesP
Name : Jamesp
Department : IT
login : RobinL
dept : Custodial
city : Denver
title : Janitor
samAccountName : RobinL
Name : RobinL
Department : Custodial
Это достаточно вычурный синтаксис, а потому давайте разберём его по частям:
-
Мы пользуемся
Select-Object
и его параметром-Property
. Мы начинаем с определения свойства*
, что означает "все имеющиеся свойства". Обратите внимание, что за*
следует запятая, что означает, что мы продолжаем свой список свойств. -
Затем мы создаём хэш- таблицу, которая является конструкцией, начинающейся с
@{
и завершающейся}
. Хэш таблицы состоят из одной или более пар ключ- значение, аSelect-Object
был запрограммирован на просмотр конкретных предоставляемых ему ключей. -
Самый первым ключом, который требуется для
Select-Object
может бытьName
,N
,Label
илиL
, а соответствующим значением для такого ключа выступает собственно название того свойства, которое мы хотим создать. В своей первой хэш таблице мы определяемsamAccountName
; во второйName
; а в третьейDepartment
. Это соответствует названиям необходимых дляNew-ADUser
параметров. -
Второй необходимый для
Select-Object
параметр может бытьExpression
либоE
. Значение для этого ключа это некий блок сценария, содержащийся внутри фигурных скобок{}
. Внутри данного блока сценария вы пользуетесь особым держателем места$_
чтобы ссылаться на уже имеющийся объект на входе из конвейера (оригинальная строка данных из нашего файла CSV), за которым следует точка. Этот держатель места$_
позволяет вам получать доступ к одному свойству направляемого конвейером объекта, или к столбцу нашего файла CSV. Он определяет значения содержимого для наших новых свойств.Попробуйте прямо сейчас Двиньтесь далее и создайте необходимый файл CSV, отображённый на Рисунке 10.12. Затем попробуйте исполнить в точности ту команду, которую мы выполнили ранее - вы можете набрать её в точности как она показана.
Что мы сделали, так это мы получили содержимое своего файла CSV - вывод из Import-CSV
- и видоизменили его, причём динамически, в своём конвейере. Наш новый вывод соответствует тому, что желает обнаружить
New-ADUser
, а потому мы теперь можем создать новых пользователей выполняя такую
команду:
PS C:\> import-csv .\newusers.csv |
>> select-object -property *,
>> @{name='samAccountName';expression={$_.login}},
>> @{label='Name';expression={$_.login}},
>> @{n='Department';e={$_.Dept}} |
>> new-aduser
>>
Получаемый синтаксис может быть слегка уродливым, но эта технология невероятно мощная. Это можно применять во многих
прочих местах PowerShell, и вы обнаружите это снова и снова в примерах, содержащихся в подсказках PowerShell; запускаем
Help Select -Example
и ищем самостоятельно.
Для всей остающейся части этой главы мы предполагаем, что у вас имеется установка Azure PowerShell. А потому давайте поработаем над этим. Если у вас нет подписки, вы можете подписаться для пробной версии здесь: https://azure.microsoft.com/en-us/free/. Если эта ссылка устарела, выполните поиск trial Azure PowerShell.
После того как вы получили подписку, убедитесь что у вас имеется установленным модуль Az. Вернитесь к Гаве 7, а соответствующая команда это
Install-Module az
Теперь, когда у вас имеется установленным Az, запустите Connect-AzAccount
и
следуйте его инструкциям: в данный момент он должен открыть браузер и ввести код. Он должен сообщить вам что вы подключены
выводя на печать вашу электронную почту, название подписки и некую иную информацию.
Когда у вас имеется множество ассоциированных с вашей учётной записью подписок, вы можете соединиться не с той подпиской.
Если это так, убедитесь что вы выбираете верную подписку. Когда названием вашей подписки выступает Visual Studio Enterprise,
ваша команды выглядит как Select-AzSubscription -SubscriptionName 'Visual Studio
Enterprise'
.