Эффективное программирование в Windows PowerShell Разбираться в Windows PowerShell и получать от него больше
Скачать 0.88 Mb.
|
25 Эксперименты показывают, что если объект содержит менее четырех свойств, PowerShell использует для его отображения таблицу. Если у объекта пять и более свойств, PowerShell выводит их в виде списка. Возможно определить несколько видов отображения для одного типа .NET. Отображение определяется в XML-файлах форматирования, расположенных в папке установки PowerShell: PS> Get-ChildItem $PSHOME\*format* Directory: Microsoft.PowerShell.Core\FileSystem::C:\Windows\System32\ WindowsPowerShell\v1.0 Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 1/24/2007 11:23 PM 22120 Certificate.format.ps1xml -a--- 1/24/2007 11:23 PM 60703 DotNetTypes.format.ps1xml -a--- 1/24/2007 11:23 PM 19730 FileSystem.format.ps1xml -a--- 1/24/2007 11:23 PM 250197 Help.format.ps1xml -a--- 1/24/2007 11:23 PM 65283 PowerShellCore.format.ps1xml -a--- 1/24/2007 11:23 PM 13394 PowerShellTrace.format.ps1xml -a--- 1/24/2007 11:23 PM 13540 Registry.format.ps1xml Содержание этих файлов выглядит следующим образом: 26 HandleCount if ($_.CPU -ne $()) { $_.CPU.ToString("N") } Id ProcessName 27 Это XML-определение табличного отображения для типа Process. Оно определяет атрибуты отображаемых колонок, данных которые в них отображаются и в некоторых случаях производит преобразование данных к более удобному отображению (например, перевод байтов в Кб или Мб). А вот определение для того же типа в для отображения в виде столбцов (Format-Wide): ProcessName В этом типе отображения выводится лишь одно свойство - ProcessName. Поискав в DotNetTypes.format.ps1xml, мы найдем больше определений. Например, именованное форматирование StartTime не вызывается по умолчанию. Вы можете применить его, указав имя в командлете Format- Table: 28 ProcessName Id HandleCount WorkingSet Зачем я всё это показываю вам? Я думаю, важно понять то "волшебство", благодаря которому объекты .NET из двоичных сущностей превращаются в текст, отображаемый в вашей консоли. Обладая этими знаниями, вы никогда не должны забывать о том, что вы имеете дело в первую очередь с объектами .NET. Существует ли простой способ выяснить, какие выводы отображения доступны для того или иного типа .NET? Да, если у вас установлены расширения PowerShell Community Extensions. PSCX предлагает удобный сценарий, написанный Joris van Lier, который называется Get-ViewDefinition и его можно использовать вот так: PS> Get-Viewdefinition System.Diagnostics.Process Name : process Path : C:\Windows\System32\WindowsPowerShell\v1.0\DotNetTypes.format.ps1xml TypeName : System.Diagnostics.Process SelectedBy : {System.Diagnostics.Process, Deserialized.System.Diagnostics.Process} GroupBy : Style : Table Name : Priority Path : C:\Windows\System32\WindowsPowerShell\v1.0\DotNetTypes.format.ps1xml TypeName : System.Diagnostics.Process 29 SelectedBy : System.Diagnostics.Process GroupBy : PriorityClass Style : Table Name : StartTime Path : C:\Windows\System32\WindowsPowerShell\v1.0\DotNetTypes.format.ps1xml TypeName : System.Diagnostics.Process SelectedBy : System.Diagnostics.Process GroupBy : Style : Table Name : process Path : C:\Windows\System32\WindowsPowerShell\v1.0\DotNetTypes.format.ps1xml TypeName : System.Diagnostics.Process SelectedBy : System.Diagnostics.Process GroupBy : Style : Wide Эти данные могут показать все доступные формы отображения. Возможно, о каких-то вы не были осведомлены. Давайте проверим эти дополнительные формы: PS> Get-Process | Format-Wide AcroRd32 ADCDLicSvc alg ati2evxx ati2evxx BTNtService ccApp CCC ccEvtMgr ccSetMgr csrss ctfmon DefWatch editplus explorer GoogleUpdate Idle lsass mdm mDNSResponder miranda32 MOM opera PnkBstrA powershell RTHDCPL Rtvscan scsiaccess PS> Get-Process | Format-Table -View Priority PriorityClass: Normal ProcessName Id HandleCount WorkingSet ----------- -- ----------- ---------- AcroRd32 3388 239 59809792 ADCDLicSvc 496 36 1212416 alg 2440 107 3584000 30 csrss 764 667 5844992 ctfmon 2732 80 3575808 DefWatch 580 55 5087232 editplus 2508 85 7794688 explorer 1716 690 17539072 PriorityClass: High ProcessName Id HandleCount WorkingSet ----------- -- ----------- ---------- winlogon 800 465 4407296 PS> Get-Process | Format-Table -View StartTime StartTime.ToShortDateString(): 21.03.2009 ProcessName Id HandleCount WorkingSet ----------- -- ----------- ---------- AcroRd32 3388 239 60645376 ADCDLicSvc 496 36 1212416 alg 2440 107 3584000 ati2evxx 1036 88 3440640 ati2evxx 1292 107 3805184 BTNtService 528 82 2449408 ccApp 2700 285 7659520 StartTime.ToShortDateString(): 01.01.1601 ProcessName Id HandleCount WorkingSet ----------- -- ----------- ---------- System 4 744 262144 StartTime.ToShortDateString(): 21.03.2009 ProcessName Id HandleCount WorkingSet ----------- -- ----------- ---------- winlogon 800 465 4407296 Что делать, если вы забыли, какие способы форматирования доступны? Главное - не забыть, что вы можете использовать Get-Command: 31 PS> Get-Command Format-* CommandType Name Definition ----------- ---- ---------- Cmdlet Format-Custom Format-Custom [[-Property] PS> Get-Date | Get-Member -Name DateTime | Format-List TypeName : System.DateTime Name : DateTime MemberType : ScriptProperty Definition : System.Object DateTime {get=if ($this.DisplayHint -ieq "Date") { "{0}" -f $this.ToLongDateString() } elseif ($this.DisplayHint -ieq "Time") { "{0}" -f $this.ToLongTimeString() } else { "{0} {1}" -f $this.ToLongDateString(), $this.ToLongTimeString() };} Теперь мы видим полное определение свойств DateTime. Замечание: часто PowerShell выводит сокращённый набор значений свойств с помощью командлета Format-List. Это делается для того, чтобы отсекать обычно ненужную информацию. Однако, если вам необходим самый подробный вывод всех деталей, используйте этот командлет так: "format-list *". Смотрите, вот стандартный вывод для объекта Process: PS> (Get-Process)[0] | Format-List Id : 3388 Handles : 230 CPU : 33,328125 Name : AcroRd32 А здесь мы запрашиваем вывод всех свойств: PS> (Get-Process)[0] | Format-List * __NounName : Process Name : AcroRd32 Handles : 230 VM : 186585088 WS : 73781248 PM : 71942144 NPM : 8760 Path : C:\Program Files\Adobe\AcroRd32.exe Company : Adobe Systems Incorporated CPU : 33,59375 FileVersion : 8.1.0.2007051100 ProductVersion : 8.1.0.2007051100 Description : Adobe Reader 8.1 33 Product : Adobe Reader Id : 3388 Понимаете, о чём я? Посмотрите, как много информации может быть не выведено, если вы символом звёздочки не укажете, что хотите видеть все свойства объекта. 34 Часть 7: Режимы синтаксического разбора PowerShell Способ, которым PowerShell осуществляет разбор строк, может удивить тех, кто ранее использовал оболочки с упрощенным парсингом, такие как CMD.EXE. Парсинг в PowerShell значительно отличается, так как он используется и как интерактивная оболочка командной строки, и как язык сценариев. Сложность заключалась в том, что разработчикам необходимо было: 1. Позволить выполнение команд и программ с аргументами в командной строке. Следствие: аргументы (имена файлов, пути) не должны требовать кавычек, если в аргументе отсутствуют пробелы. 2. Позволять выполнение сценариев, содержащих выражения, которые аналогичны выражениям в большинстве языков скриптов/программирования. Следствие: Сценарий PowerShell должен верно определять выражения вида 2 + 2 и $date.Second, а также строки, использующие кавычки типа "del -r * is being executed". 3. Предоставить возможность копирования интерактивно введенного кода и вставки его в сценарий для последующего применения. Следствие: эти две области применения - интерактивная и скриптовая - должны "уживаться". Частью мощного языка сценариев должна являться поддержка не только данных строкового типа. Фактически PowerShell поддерживает большинство типов .NET, включая String, Int8, Int16, Int32, Decimal, Single, Double, Boolean, Array, ArrayList, StringBuilder и многие другие .NET-типы. Звучит неплохо, но всё же - что там с режимами разбора? Давайте подумаем, как язык может представлять строчный литерал? Ну, большинство, как правило, ожидают эту строку: "Hello World". Фактически, PowerShell распознаёт её как строку: PS> "Hello World".GetType().Name String PS> "Hello World" Hello World Если вы напечатаете строку сразу за приглашением и нажмёте клавишу Enter, PowerShell, благодаря среде REPL(Read-eval-print-loop), отобразит её в консоли, как показано выше. А если необходимо указать аргумент для команды в кавычках? PS> del "foo.txt", "bar.txt", "baz.txt" Вы сразу ощутите отличия от других оболочек. Хуже того, эти кавычки довольно быстро начинают надоедать. Я думаю, что разработчики PowerShell вовремя решили, что им понадобятся два различных режима синтаксического анализа строк. Во-первых, необходимо разбирать строки так, как это делают традиционные оболочки, в которых имена файлов и процессов, путей не обрамляются кавычками. Во- вторых, необходимо иметь возможность воспринимать строковое выражение так, как это принято в языках программирования - заключая его в кавычки. В PowerShell первый режим называется режим разбора команд, а второй - режим разбора выражений. Важно понимать, в каком режиме вы находитесь в данный момент, и ещё важней - как переключаться между ними. Давайте взглянем на пример. Очевидно, хотим ввести команду для удаления этих файлов: PS> del foo.txt, bar.txt, baz.txt Так даже лучше - для имён файлов кавычки не требуются. PowerShell обрабатывает эти имена как строковые выражения даже без кавычек в режиме команд. А что случится, если путь будет содержать пробелы? Вы можете сами попробовать это: 35 PS> del 'C:\Documents and Settings\Keith\_lesshst' И это работает так, как и ожидалось. А если необходимо выполнить программу с пробелами в пути? PS> 'C:\Program Files\Windows NT\Accessories\wordpad.exe' C:\Program Files\Windows NT\Accessories\wordpad.exe Такая конструкция не работает. PowerShell решил, что мы ввели строку и он просто отобразил её на экране. Это произошло потому, что PowerShell находится в режиме выражений. Нам необходимо сообщить ему, что эту строку необходимо анализировать в режиме команд. Для этого используется оператор '&': PS> & 'C:\Program Files\Windows NT\Accessories\wordpad.exe' Подсказка: для автоматического завершения части пути используйте Tab и Shift-Tab. Если путь содержит пробелы, PowerShell также вставит оператор вызова и автоматически поставит кавычки вокруг пути. Этот пример показывает, что PowerShell анализирует первый не пустой символ в строке для определения, в каком режиме строка будет обработана. Любой из нижеприведённых символов переводит PowerShell в режим команд: [_aA-zZ] & \ У этого правила есть одно исключение - если строка начинается с ключевого слова (if, do, while, foreach и т. д.), PowerShell переходит в режим выражений и будет ожидать оставшуюся часть выражения, связанную с этим ключевым словом. Преимущества командного режима: • Строка не нуждается в обрамлении кавычками, если она не содержит пробелы • Числа обрабатываются как числа, все другие аргументы - как строки, за исключением тех, которые начинаются с @, $, (, ' или ". Числа интерпретируются как Int32, Int64, Double или Decimal в зависимости от их написания и величины, необходимой для хранения числа, например, 12, 30GB, 1E-3, 100.01d. Хорошо, а для чего нужен режим выражений? Ну, как я уже говорил, необходимо иметь возможность оценки выражений, таких, как это: PS> 64-2 62 Не удивительно, что некоторые оболочки могут интерпретировать это выражение, например, попытавшись выполнить команду с именем '64-2'. Как же PowerShell определяет, что строку необходимо анализировать в режиме выражений? Если строка начинается с цифры, или одного из этих знаков - @, $, (, ' или " - строка разбирается в режиме выражений. Преимущества этого режима: 36 • Устранение возможности неверного толкования команд как строк, например, del -recurse * - это команда, а "del -recurse *" - это строка. • Прямое указание арифметических операций и сопоставления выражений, например 64-2 (62) и $array.count -gt 100. В режиме команд -gt интерпретировалось бы как параметр, если предыдущая лексема соответствует верной команде. Одним из следствий правил режима анализа строк является то, что для запуска исполняемых файлов или сценариев с именами, начинающимися с цифры, необходимо заключать их в кавычки и использовать оператор вызова: PS> & '64E1' Если вы попытаетесь выполнить 64E1 без оператора вызова, PowerShell не сможет понять, хотите вы ввести число 64E1(640) или выполнить исполняемый файл с именем 64E1.exe или 64E1.ps1. В этом случае зависит от вас, в какой режим анализа перейдет PowerShell. Примечание: я заметил, что в случае указания полного имени (например, 64E1.ps1 или 64E1.exe), нет необходимости заключать команду в кавычки. А что делать, если в одной строке необходимо сочетать различные режимы? Легко. Просто используйте выражения группировки (), подвыражение $() или подвыражение массива @(). Это заставит парсер производить повторную оценку режима синтаксического анализа, который будет произведён по первому символу внутри скобок. Чем различаются выражения группировки (), подвыражения $() и подвыражения массива @()? Выражение группировки может содержать лишь простое выражение или простой конвейер. Подвыражение может содержать несколько операторов, разделённых точкой с запятой. Вывод каждого оператора передаётся на вывод подвыражения, который может быть скаляром, коллекцией или пустым множеством. Массив подвыражений ведет себя точно так же, как подвыражение, за исключением того, что он гарантирует, что выводом будет массив. Два случая, имеющих различия: 1. Когда массив не имеет вывода, подвыражение вернёт пустой массив 2. Если результатом является скалярное значение, будет выведен массив с одним элементом, содержащим скалярную величину. Если выводом уже является массив, то использование оператора массива не действует, то есть массив не будет ещё раз "обёрнут" в другой массив. В следующем примере я внедрю команду "Get-ChildItem C:\Windows" в строку, разбор которой начинается в режиме выражений. Когда будет встречено выражение группировки (get-childitem c:\windows), начнётся новая оценка режима анализа. Обнаружив, что первым символом является 'g', парсер перейдёт в режим команд до конца текста внутри выражения группировки. Замечу, что ".Lehgth" анализируется в режиме выражений, поскольку не входит в выражение группировки, и PowerShell вернется обратно к предыдущему режиму парсинга. ".Lehgth" заставляет PowerShell получить свойство Lehgth для объекта, выведенного выражением группировки. В моём случае, это массив объектов FileInfo и DirectoryInfo. Свойство Lehgth сообщит количество элементов в этом массиве. PS> 10 + (get-childitem c:\windows).Length 115 Мы можем сделать наоборот. То есть, вставить выражение в строку, разбор которой начинается в командном режиме. В примере ниже мы используем выражение, вычисляющее количество объектов, выбранных из последовательности. |