Pull to refresh

Comments 21

Побольше бы таких доходчивых и полезных статей на Хабре.
Без понтов и зашкаливающего эго.

Стоит, наверное, отметить, что массив object[] можно скастовать к конкретному типу, например, int[], если каждый элемент можно преобразовать.


А еще PowerShell настолько интегрирован с dotnet, что одних массивов может быть недостаточно. Можно дергать и другие коллекции:


> $coll = [System.Collections.Generic.List[int]]::New()
> $coll.Add(5)
> $coll
> 5

Или даже так:


> [System.Linq.Enumerable]::Range(0, 10).Select([System.Func[int, int]]{param($x) $x * $x}) | Join-string -Sep ", "
> 0, 1, 4, 9, 16, 25, 36, 49, 64, 81
Что-то не получается.
[System.Linq.Enumerable]::Range(0, 10).Select([System.Func[int, int]]{param($x) $x * $x}) Сбой вызова метода из-за отсутствия в [System.Int32] метода с именем "Select".
строка:1 знак:1
+ ([System.Linq.Enumerable]::Range(0, 10)).Select([System.Func[int, int ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound

Хм, интересно. Ответ оказывается простым.
Я тестировал это в PowerShell Core 7.0-rc3 (stable-версия вроде только что вышла)
В обычном PowerShell это действительно разваливается.


В full-framework тип RangeIterator, который возвращается этим методом, имеет буквально несколько определенных методов. Проверить легко: запустите интерпретатор csi.exe. и выполните код:


System.Linq.Enumerable.Range(0, 1)
  .GetType()
  .GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
  .Select(x => x.Name)
  .Aggregate((old, @new) => $"{old}, {@new}")

Я получаю такой вывод:


"Equals, GetHashCode, GetType, ToString"

Теперь то-же самое для dotnet-script интерпретатора. Вывод следующий:


"Clone, MoveNext, Dispose, Select, ToArray, ToList, GetCount, Skip, Take, TryGetElementAt, TryGetFirst, TryGetLast, get_Current, GetEnumerator, Where, ToString, Equals, GetHashCode, GetType"

Т.е в .net core у вас определен инстанс-метод


IEnumerable<TResult> RangeIterator.Select<TSource, TResult>(Func<TSource, TResult> selector)

который и вызывается. В full framework такого метода нет и вместо него должен вызываться extension-метод


IEnumerable<TResult> Enumerable.Select<TSource, TResult>(this IEnumerable<TSource> @this, Func<TSource, TResult> selector)

и я полагаю PowerShell такие методы не понимает. Но это можно исправить вот так:


[System.Linq.Enumerable]::Select([System.Linq.Enumerable]::Range(0, 10), [Func[int, string]]{param($x) ($x * $x).ToString()}) -join ", "

В легаси PowerShell позходу и Join-String нет, но вот это выражение у меня работало и в PSCore 7.0.0-rc3, и в PS 5.1.x.

Не получается, потому что Linq построен на методах раширения (extention method), позволяющих вызывать в качестве методов экземпляров класса методы, специальным образом определеные в другом классе. А в Powershell этот механизм не реализован. В нем соответсвующие методы нужно вызывать явно, через тот класс, в котором они реально определены.
В Powershell для реализации аналогичной функциональности используется другой механизм, свойственный как раз shell-языкам: конвейер и команды (commandlet), которые принимают коллекции по конвейеру на вход, обрабатывают их элементы (или даже коллекцию целиком, например — сортируют ее) определенным образом и передают результат по конвейеру на выход. Скрипты, если они написаны определенным образом, тоже умеют так работать, в качестве таких команд-фильтров.
Кстати, у Powershell есть в этом отношениии еще одна особенность. На выходе из конвейера наружу результаты выглядят как массив, если их несколько, или как одиночный элемент, если элемент один. Такое поведение удобно для интерактивной работы, но вот написание скриптов, наоборот, затрудняет.
Спасибо за статью. Хотелось бы почитать подробнее про командлет Start-Process и его параметр -Wait, абсолютно не понятно почему это работает с одними файлами инсталляторов и не работает с другими. Уже дошел до чтения кода функции, часто ей пользуюсь и каждый раз это лотерея.

p.s .WaitForExit() не предлагать:)

Некоторые "инсталяторы" распаковывают настоящий инсталятор, запускают его и сразу после этого завершают работу. В с ними-то никакой -Wait и не работает.

Да, с этим я и столкнулся, если передавать переменную процесса сразу в .WaitForExit() он берет только первый процесс из цепочки и радостно рапортует о завершении цикла, если использовать честный -Wait то он ждет пока все процессы цепочки завершатся, а самое интересное что инсталлятор не завершает их все и такой -Wait висит вечно. В итоге я сейчас просто ловлю пока в процессах появится определенный и грохаю ожидание. Какой-то парень предлагал написать таймаут для этого -Wait, даже написал, но его заклевали на гитхабе и PR закрыт.
Язык PS уродлив.

Как прочитавший который десяток спецификаций разных языков (см.публикации) скажу. Только эго заставило МС заменить вполне приличный c/jscript в системной командной строке
Посмотрел кратко ваши публикации и не увидел, чтобы вы рассматривали хоть один язык командной оболочки (shell). Из чего заключаю, что вы просто оцениваете Powershell не в том контексте применения, для которого он предназначен.
Так вот, для языка комнадной оболочки написание программ (скриптов) — это только одна область примения, и не факт, что самая важная. Другая очень важная область применения такого языка — выдача интерактивных команд, причем часто — не одиночных команд, а их связок: взять выдачу одной команды, передать на вход другой для преобразования и т.д. Причем, часто бывает желательным, чтобы вся связка умещалась на одной строке — чтобы было проще вернуть её из истории (стрелкой вверх, например) и выполнить заново.
Лично мне, например, как системному администратору MS Exchange по основному роду занятий, такие вещи приходится проделывать очень часто.
Поэтому сложные конструкции из нескольких команд — типа набора операторов присваивания, операторов цикла — для интерактивной работы подходят плохо. А в языках командной оболочки для этой цели есть очень удобное средство — конвейер (pipe).
Есть и другие особенности Powershell — как свойственные многим другим языкам командной оболочки, так и уникальные именно для него — которые облегчают именно интерактивное выпонение команд. Надеюсь, авторы этой серии о них расскажут.
А вот у меня лично, основные притензии именно к «интерактивным командам».
1. Длинные команды которые «не хочется» использовать.
Что бы найти нужный процесс, остановить запустить сервис или найти файл я не лезу в PS и т.д. и лично я, ещё не разу не видел, что бы кто то лез в PS, что бы наспех получить информацию от системы. Если единственный способ получить нужные данные, это PS апплет, тогда лезу в PS.
2. Медленный. Первый старт иногда приходится секунд 20 ждать, да даже задержка 3-5 секунд напрягает. Тот же cmd стартует мгновенно всегда. Я понимаю что объектная модель и все такое… но для «интерактивной работы» запуск оболочки должен быть мгновенный. У меня небольшой опыт в скриптописании, но все, что я делал на PS, python и bash, на PS всегда было очень медленно.
3. Риторический вопрос: Почему так поздно взялись за него?
  1. Длинные команды которые «не хочется» использовать.

Есть автокомплит и алиасы


Медленный. Первый старт иногда приходится секунд 20 ждать, да даже задержка 3-5 секунд напрягает.

Как вы этого добиваетесь? У меня разница в старте между cmd и ps визуально неразличима (и то и другое запускается мгновенно). Вы, наверное, про запуск ISE? Она, да, стартует секунды две. Но вас же никто не заставляет использовать ISE, можете запускать в любом терминале на свой выбор.

Старт pscore начиная от 6й версии действительно очень медленный — на gihub по-этому поводу много issues. Одна только загрузка в терминале бывает длится секунду, и уж точно не мгновенно — это очень раздражает.

Это специфика shell языков. Они все такие не от хорошей жизни. Основное назначение этой группы языков — командный интерпретатор в консоли.
Сложно совместить удобство работы в консоли с удобством написания скриптов.
вызов функций в PowerShell может показаться непривычным: аргументы (если они есть) не заключаются в круглые скобки и разделяются пробелами

А я вызываю так и все работает:
MyFunction($myVar)

Просто у вас аргументом функции становится не $myVar, а ($myVar)

Именно. Причем, если так — в скобках через запятую — захотеть передать два аргумента, то внезапно будет передан один — массив из двух элементов.
PS А вот к вызовам методов объектов это не относится — методы вызываются именно со списком аргументов в скобках через запятую.
UFO just landed and posted this here
Sign up to leave a comment.