elglin: (Default)
[personal profile] elglin
То, как повершелл ведет себя с возвратом из функций меня СУКА БЕСИТ.
Языки здорового человека либо строго типизируют возврат (C; внезапно bash), либо никак не типизируют (Python). В повершелле можно указать тип возврата директивой OutputType... но она ни к чему не обязывает, я могу указать String и вернуть int.
Далее, языки здорового человека (C, Python) имеют команду return - вот что ей дал, то функция и вернет. Языки здорового человека со странностями (VB, емнип Pascal) это делают путем присвоения значения возврата "имени функции". Шелл здорового человека (bash) умеет возвращать только код возврата - но там функции могут писать в stdout, для перехвата которого есть штатный конструкт (где-то здесь порылся срач между $() и ``).
Сраный повершелл возвращает все, что кем угодно было выведено в stdout ПЛЮС то, что вернули return - этакая ужасная помесь процедурного языка и шелла. Если было несколько такого дела, возвращается массив, элементами которого являются выводы - даже если этот вывод $null. Вот только что на выходе из функции, которая должна возвращать НИЧЕГО, я поимел @($null, $null) и с горящей задницей искал, откуда это лезет. Нашел и пофиксил, конечно, но осадок остался.

Мораль сей басни такова: если вы пишете на PSH не в шелл-стиле и не в BEGIN-PROCESS-END стиле, а в старом добром процедурном (если вы в анамнезе решеточник, питонист или, оужас, сишник), то внимательно следите за выводом. Вызов любого командлета или функции, которые хотя бы теоретически могут что-то вернуть, но который вам нафиг не нужен, должен быть либо присвоен мусорной переменной, либо отправлен конвейером в Out-Null. Иначе дебаггинг гарантирован.

Да, чтобы два раза не вставать, ну вот на хрена запилено пицот (ладно, 5 или 6) потоков вывода вместо старых добрых stderr и stdout, если нельзя сделать | Out-Verbose или | Out-Error , а только Write-Error или Write-Verbose - и это при том, что "конвейером объектов" размахивают все адепты.

(no subject)

Date: 2018-08-31 11:19 am (UTC)
scif_yar: (Default)
From: [personal profile] scif_yar
>>Сраный повершелл возвращает все, что кем угодно было выведено в stdout ПЛЮС то, что вернули return
-
Правда чтоле?
function goga1
{

write-host 34566

$123 = "N1"

return $123
}
$a1 = @(goga1)
cls

write-host $a1

Что будет на экране ?

(no subject)

Date: 2018-08-31 03:17 pm (UTC)
scif_yar: (Default)
From: [personal profile] scif_yar
function foo1 {
Get-Location
return "plugh"
}

$foo = @(foo1)
cls
$foo[-1]

(но это я уже нагуглил) https://stackoverflow.com/questions/10286164/function-return-value-in-powershell/10288256

(no subject)

Date: 2018-08-31 11:21 am (UTC)
scif_yar: (Default)
From: [personal profile] scif_yar
>>Вызов любого командлета или функции, которые хотя бы теоретически могут что-то вернуть, но который вам нафиг не нужен, должен быть либо присвоен мусорной переменной, либо отправлен конвейером в Out-Null.
-
$a1 = @(goga1) и нефиг

(no subject)

Date: 2018-08-31 03:19 pm (UTC)
scif_yar: (Default)
From: [personal profile] scif_yar
>>работать с возвратом любой функции как с массивом - это, имхо, извращение.
-
вопрос привычки. Я пару раз так наябывался, что есть сервер с 1 сервисом foo1, а есть с двумя, foo1 и foo2. И ну его разбираться чего мне там вернут - один сервис или пачку.

(no subject)

Date: 2019-03-05 11:38 pm (UTC)
pan_2: (Default)
From: [personal profile] pan_2
Это пока у тебя не будет полтыщи объектов в возврате.

(no subject)

Date: 2019-03-07 06:17 pm (UTC)
pan_2: (Default)
From: [personal profile] pan_2
Тут есть нюанс в том, что Пош зачастую пытается упростить человеку жизнь (опять-таки - потому что он для этого и был задуман). В комменте ниже я привёл пример, где, в зависимости от количества возвращённых объектов, будет либо [object] либо [object[]]. Многие по незнанию на это напарываются.

(no subject)

Date: 2019-03-06 12:20 am (UTC)
pan_2: (Default)
From: [personal profile] pan_2
И да, рекомендую помедитировать над выводом
(gci $env:windir -Filter win*).Length
(gci -Path $env:windir -Filter winhlp*).Length
@(gci -Path $env:windir -Filter winhlp*).Length

(no subject)

Date: 2019-03-06 12:12 am (UTC)
pan_2: (Default)
From: [personal profile] pan_2
> Языки здорового человека либо строго типизируют возврат
Это костные языки старых язвенников, место которым в суперсекурном низу. Нахер оно не нужно в скриптовом языке - это бы дико ограничивало возможности.
Банальнейший пример - [string] и [string[]]

> старых добрых stderr и stdout
Потому что они сраное говно.

>ПЛЮС то, что вернули return
Это потому что кто-то нихера не читал мануал:
get-help about_Return

РАЗДЕЛ
    about_Return

КРАТКОЕ ОПИСАНИЕ
    Выход из текущей области действия, которая может быть функцией, 
    скриптом или блоком скриптов.


ПОЛНОЕ ОПИСАНИЕ
    Ключевое слово Return служит для выхода из функции, скрипта или блока 
    скриптов. Его можно использовать для выхода из области действия в 
    определенной точке, для вывода значения или для указания на окончание 
    области действия. 


    Пользователи, знакомые с языками, подобными C или C#, могут 
    использовать ключевое слово Return, чтобы сделать явной логику 
    выхода из области действия.
 

    В Windows PowerShell результаты выражений выводятся даже при 
    отсутствии выражений с ключевым словом Return. В таких языках 
    как C или C# выводится только значение или значения, указанные 
    ключевым словом Return.
	


Если недоходит, перевожу:

НЕ НАДО использовать return для вывода результата функции. В идеале вообще return не должно быть в коде.


>даже если этот вывод $null
$null -ne пустота

Если ты словил $null в выводе функции - это значит тот, кто эту функцию писал - крупно облажался. Нет, не потому что "не проверил а не $null ли идёт в вывод", а потому что если нихрена не надо выводить в пайплайн - то и не надо ничего выводить. А если таки пришёл $null - значит кто-то создал пустой объект, ничего с ним не сделал, а потом швырнул в пайплайн.

>если вы пишете на PSH не в шелл-стиле
если вы пишите на PS в стиле убогово текстового говна 40 летней давности - не надо удивляться, что что-то идёт не так как привыкли.

>но который вам нафиг не нужен, должен быть либо присвоен мусорной переменной, либо отправлен конвейером в Out-Null. Иначе дебаггинг гарантирован.
Старый язвенник детектед.
Нужно проверять что пришло. Если должен придти объект нужного типа - то проверять тип, если объект с определённым свойством - то
if ($object.property) {}
.
Ах, да, старые язвенники часто пишут херню вида
$var = Get-Something
$foo = $var + 'SomethingElse'
return $foo
в результате получают 'SomethingElse' вместо $var + 'SomethingElse', хотя достаточно было бы, например, сделать if или ещё лучше 
foreach ($thisVar in $var) {
	$thisVar + 'SomethingElse'
	}
В выводе ВСЕГДА будет $thisVar + 'SomethingElse', потому что если Get-Something облажался, или ничего не вернул - то ветка % просто не будет выполнена => не будет мусора в выводе. > если нельзя сделать | Out-Verbose или | Out-Error
get-help Write-Error -Parameter Message

-Message 
    Задает текст сообщения об ошибке.  Если текст содержит пробелы или специальные символы, е
    го нужно заключить в кавычки. Кроме того, строку сообщения можно передать командлету Writ
    e-Error по конвейеру.
    
    Требуется?                    true
    Позиция?                    1
    Значение по умолчанию                Нет
    Принимать входные данные конвейера?true (ByValue)
    Принимать подстановочные знаки?false
Если не доходит, то нужно сделать
"THIS IS A FUCKING ERROR THROUGH THE PIPELINE" | Write-Error
в ближайшем пошике.

(no subject)

Date: 2019-03-07 06:11 pm (UTC)
pan_2: (Default)
From: [personal profile] pan_2
> Я так чувствую, что вы пришли посраться. Ну давайте посремся, время от времени это, говорят, терапевтически полезно.
Ну почему посраться - так, поднасрать ласково, да поучить уму-разуму. Можно на ты, чай не первый раз пути пересекаются.

> Моя позиция в том, что мы либо типизируем вывод, либо нет
Ну дихотомия мировоззрений это прекрасно, конечно, но есть сермяжная рутина. Я уже говорил про то, что PS - скриптовый язык, и ему костность и академичность - вредна.
Для начала посмотрим в офдоку: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_outputtypeattribute?view=powershell-6
The OutputType attribute value is only a documentation note. It is not derived from the function code or compared to the actual function output. As such, the value might be inaccurate.
Упс, нет там сторогой типизации вывода вообще. Этот параметр сугубо для человеков (и для всяких IntelliSense) для документации.

> не познал дзен
Дзен простой - нет никакого смысла строго типизировать вывод. Почему? Потому что это скриптовый, интерактивный язык для решения рутинных задач, на котором пишут в самом натуральном Agile стиле, да так, что все Agile программизды плачут горячими слезами: надо что-то решить СЕЙЧАС - вот консоль/нотепад/ISE, пиши дебажь/рефлексируй интерактивно, решай задачу. А не как по классике, нарисуй UML, построй классы, собери три тысячи конструкторов...

Я выше привёл пример с массивами: это хорошая иллюстрация, почему строгая типизация вывода бессмысленна в поше.

Т.е. к примеру делаем функцию
function Get-WinAsteriskFiles {
[CmdletBinding()]
[OutputType([System.IO.FileInfo])]
Param()
    gci -Path $env:windir -Filter Win*
    }
$gci = Get-WinAsteriskFiles

По идее, мы должны получать только FileInfo, и ($gci[$gci.GetUpperBound(0)]).GetType() нам возвращает то что мы хотим:
IsPublic IsSerial Name                                     BaseType                          
-------- -------- ----                                     --------                          
True     True     FileInfo                                 System.IO.FileSystemInfo

Но вот что делать, если у нас там не только [System.IO.FileInfo]?
И вот простой пример тут же:($gci[0]).GetType()
IsPublic IsSerial Name                                     BaseType                          
-------- -------- ----                                     --------                          
True     True     DirectoryInfo                            System.IO.FileSystemInfo

Упс, уже DirectoryInfo. Вот что с ним сделать? Матернуться и стопорнуть эксепшеном? А что с остальными делать, где тип соответствует? Продолжить их выкидывать в пайплайн? Не продолжать?
А что делать если у нас filter/advanced function и мы вообще в блоке process { } это всё обрабатываем? А есть ещё автомагия приведения типов, и тут ещё на пару Кб можно настрочить примеров.

В общем - нафиг это всё не нужно (в поше).
Если сильно хочется ООП и типизации - то это делается ОЧЕНЬ просто.
Вариант А:
Когда выкидываем результат наружу - жёстко кастуем его, [int]$FuncOutput
Вариант Б:
На входе функций чётко указываем какие типы принимают параметры.
Всё, проблема решена.*

> Короче, мне надо было работать с конкретным API с его идиосинкразиями
Ну так это же частности, а не стандартное поведение. Я когда работал с HPE iLO Cmdlets - тоже много и забористо матерился, но проблема то была не в поше, а в том, что писали этот сборник куча разных людей.

> возвращать $null не плохо и не хорошо. Вопрос только в том
Безусловно. И пош тут не указ совершенно.

> во-вторых, когда у вас во-от такенная кодобаза
Для этого есть куча вариантов, начиная с Plaster для нового кода и заканчивая какеготамнепомнюсейчас который будет орать, если ретурн есть в коде.
Т.е. вопрос даже не в том, что ретурн плохо, а-яй-яй, а в том, что бы старые привычки понимали, что в нём уже нет необходимости.

> Касательно убогого текстового говна
Это всё прекрасно, но нужно чётко понимать, что всё это великолепие - результат ограничений систем 40 летней давности. Да, много всего прекрасного (и не очень), но это всё крутится вокруг текста и совершенно не имеет смысла, когда есть возможность работать с объектами и свойствами. Поэтому человек, который будет писать в поше в шелл-стайл будет страдать, материться и постоянно вопить "а на кой ляд мне этот пош вообще сдался, я лучше *sh поставлю и всё по привычке сделаю". И не материться он сможет только познав дзен научившись новому.
Я, собсно, переводил шелл-скрипты в пош, как прямым методом (написав на поше парсер), так и ручками, переписывая логику. И сравнивая до и после - видно, что в шелл-скриптах постоянно приходится на ровном месте делать кучу телодвижений, вызванных именно ограничениями самого шелла и парадигмы "всё текст"**.

> Старые язвенники пишут if($var = Get-Something)
Ага, а потом плачутся в бложеках, что что-то пошло не так. Особенно весело когда if ($var = Get-Something) { Do-Something } else { Handle-Error }, а Get-Something выдаёт stop exception. Проходили, не стоит так делать.

> спустя пару недель после поста я осознал, что они вполне себе принимают конвейер
Отлично, значит дзен постигается потихоньку.

> а я просто яд сцеживал, то переписывать смысла не видел
Вполне разумный подход. Иногда правда это вызывает демонов (хе-хе), но это частности.

> без проблем признаю свою ошибку
Да по сути - это даже не ошибка, просто незнание.

> холиваров
Тут вопрос не холивара, тут вопрос понимания. Я с пошем с V1, и имею тайное знание знаю много нюансов, которые человеку "со стороны" просто неизвестны, просто потому что он сейчас с ним познакомился, а не десять лет назад. Соответственно, зачастую могу объяснить "почему так".
В том числе, почему, по сравнению с пошем, шелл - текстовая какашка.

>Так что предлагаю градус дискуссии все же понизить, и срач на этом закончить
Я в первом (и тем что выше) абзаце уже обозначил свою позицию, но конкретизирую:
я за конструктивный срач, который будет полезен обоим участником; и я не из тех людей, у которых повышенная выработка серотонина при макании людей в грязь (иначе бы первый коммент был из целиком из "ЛОХ, RTFM"). Но я категорически нескромен и фамильярен, и моя манера общения у некоторых может вызывать оторопь.
Поскольку "у нас свободная страна", захотите меня спросить - велкам, не захотите - велкам.

ЗЫ: сейчас вспомнил - была у меня какая-то задачка, что в зависимости от условий выполнения функция выкидывала совершенно разные типы. Не скажу что это здравый подход, но строгая типизация в этом случае заставила бы очень сильно раздувать код. Из примеров на вскидку могу предложить варианты с путями - если путь ещё не существует, то не получится вернуть [System.IO.FileSystemInfo], только [string]; если же есть - то можно сразу с готовым объектом работать.

* да, только в своём коде. Но опять-таки, если приходится работать с чужим дурным кодом (который может быть вообще в .dll скомпилен, так что даже исходник не глянешь), то пош предоставляет возможность выдернуть (через тот же GetType(), например) только то, что нужно.

** моя любимая загадка на этот счёт - какой errorlevel будет, если yum сможет поставить только часть пакетов, указанных ему для установки. И вторая задача - как понять что именно не поставилось.

(no subject)

Date: 2019-03-07 08:17 pm (UTC)
pan_2: (Default)
From: [personal profile] pan_2
> Я подозреваю, что ее считывает Get-Help по командлету, но все равно странно
Именно. Так же юзает всякий IntelliSense для посказок, бывает полезно.

>наличие связки баш-питон
Баш-питон2 или Баш-питон3? ;)

>настраивает меня критически против желающих тащить пош на линух.
Пош на линуксе мне хорош тем, что я по прежнему могу работать с объектами. Т.е. мне, конечно, пришлось сначала регескпами парсить тот же yum, зато после этого я точно могу сказать что стоит, что нет. Не то, чтобы это было сильно нужно в реальной жизни (один хрен ты будешь ставить всё что нужно, и пока не поставишь - дальше не пойдёшь), но удобнее во сто крат.

>А вот с исключениями вообще боль
Ещё больнее будет, когда будет понятно, что часть вещей стопорятся, например когда нет связи с сервером, а часть нет - выдают кто $null, а кто и полноценный объект с Count = 0...

>вот это процедурное мышление, возможно, и объясняет мою любовь к return. Это офигенно дисциплинирует тебя
Ну вот поэтому return в поше и есть.

>А с yum пример хороший
А с этим ещё веселей. Если ни один пакет не поставился (их тупо нет, репа не подключена) - вернёт "плохой" эррорлевел; а если хоть один пакет поставился - вернёт "хороший" эррорлевел. Т.е. по errorlevel Ты просто никогда не узнаешь, что что-то непоставилось. Просто потому, что у тебя только 1 метод сообщения информации.
Page generated Jan. 22nd, 2026 09:15 pm
Powered by Dreamwidth Studios