Мне часто приходится пользоваться PowerShell. Конечно, его создатели не имели никакого представления о прекрасном и эстетике. Уродливость PowerShell особенна видна при его сравнении, например, с Python. С другой стороны, как говорится, c лица не воду пить - работает и хорошо? Но нет, мне кажется в PowerShell есть по крайней мере пара моментов, которые фатально влияют на его практическое применение.
Расхлябанность
Это пока не фатальный пункт. Даже по сравнению с Python строгости меньше, мы можем забыть присвоить хоть какое-то значение переменной:
PS C:\> Write-host "Value is: $newvar"
Value is:
PS C:\>
Впрочем, даже ключевые слова иногда не проверяются:

Проблемы с наборами из одного элемента
Запишем файл и прочитаем его:
@"
One
Two
Three
"@ | Out-File x.tmp
$f = Get-Content "x.tmp"
Write-Host "Inside the file: $f"
Output:
Inside the file: One Two Three
Get-Content выдает массив строк. Расхлябанность языка приводит к тому, что при выводе они склеиваются - хорошо что хоть через пробел. Поэтому если мы хотим вывести первую строку, то нет проблем:
@"
One
Two
Three
"@ | Out-File x.tmp
$f = (Get-Content "x.tmp")[0]
Write-Host "Inside the file: $f"
Output:
Inside the file: One
Ну или если поменять файл:
@"
One
"@ | Out-File x2.tmp
$f = (Get-Content "x2.tmp")[0]
Write-Host "Inside the file: $f"
Output:
Inside the file: O
Что, простите? Куда делись 'ne'? Ответ шокирует:
(Get-Content "x.tmp").GetType()
(Get-Content "x2.tmp").GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
True True String System.Object
То есть раз в файле есть только одна строка, то зачем заморачиваться массивом, выбросим скобки и оставим только один элемент. Просто замечательно. Экспериментально устанавливаем с помощью эксперимента в notepad, что string, string<br> это однострочный файл, а вот string<br><br> или string<br><sp> уже многострочные файлы - при этом в notepad все эти варианты визуально неотличимы друг от друга!
Теперь постараемся написать программу, которая выводит первую строку файла:
$f = Get-Content "x.tmp"
if ($f.GetType().Name -eq "Object[]") {
$firstln = $f[0]
} else {
$firstln = $f
}
Write-Host $firstln
Правильно? Нет! Если файл существует, но первая строка пуста (там нет даже пробела), то Get-Content возвращает $null! Кстати, найдите в официальной документации Microsoft описание этого поведения. Мне не удалось.
Это не единичный случай
Теперь вас не удивит поведение следующего кода:
$found = invoke-Sqlcmd -ServerInstance "Localhost" -Query "select * from sysdatabases"
$found.getType()
$found = invoke-Sqlcmd -ServerInstance "Localhost" -Query "select top 1 * from sysdatabases"
$found.getType()
$found = invoke-Sqlcmd -ServerInstance "Localhost" -Query "select top 0 * from sysdatabases"
$found.getType()

У меня в коде встречается, например:
$cnt = $found.Rows.Count
if ($found.getType().FullName -eq 'System.Management.Automation.PSCustomObject')
{ $cnt = 1; }
Программисты Python с завистью глядят на элегантность этого кода, а потом нервно курят в сторонке, понимая, что они лишены эстетического начала и жизнь прошла зря.
Где-то плачут математики, для которых множество из двух элементов, и одного, и нуля элементов - это объекты одного "типа". Но создатели PowerShell похоже считают в парадигме: ноль, один, много.
Контрольный выстрел
Вам еще хочется программировать на PowerShell? Давайте поговорим об этом. Вот еще пример:
function something([string]$file, [string]$p2)
{
New-item $file
return "always OK"
}
$res = something "xxx.tmp" "beta"
Write-host "Result: $res"
$res.GetType()
Что будет в $res? Вы уверены? Давайте проверим:

Какого черта? На самом деле в PowerShell функция возвращает не только то, что в return (это ключевое слово можно и не писать), а собирает все выводы по ходу выполнения. Вывод new-Item прилепился до того, что мы вывели в return.
Если в данном случае мы можем исправить это, дописав | Out-Null после New-Item, то при вызове произвольной сложной функции, написанной Васей Пупкиным, вы вообще не можете ничего гарантировать. Вообще ни-че-го.
По работе и в своем пет проекте мне пришлось написать много скриптов на PowerShell. В какой-то момент я решил замахнуться на Ubuntu и переписал огромное количество скриптов для MSSQL, Postgre и MySQL с PowerShell на Python. Когда это заработало под Ubuntu, я не мог уже остановиться, поставил pwsh для Ubuntu и убедился, что PowerShell хорошо под ней работает, и даже имитирует что все case-insensitive. Наконец я проверил Python-версию скриптов под Windows и только тогда успокоил ее величество ортогональность.
Кстати, Python скрипт везде стартует быстрее. PowerShell тупит доли секунды. Обычно это неважно, но когда на любое действие GUI вызывается скрипт то разница чувствуется.
Комментарии (31)
gatoazul
00.00.0000 00:00+3Стоит еще упомянуть, что просто так скрипт на Powershell вообще не запустишь, надо или прописывать какие-то непонятные заклинания в реестр, или ставить не менее непонятные ключи при вызове.
Кто мешает проставить их же злоумышленникам - вообще не понятно.
HemulGM
00.00.0000 00:00О чем именно речь? Всегда запускал скрипты без всяких проблем
Tzimie Автор
00.00.0000 00:00+4Execution policy.
HemulGM
00.00.0000 00:00+1Одна строчка
Set-ExecutionPolicy Unrestricted
NAI
00.00.0000 00:00... и корпоративная безопасность после этого выходит из чата.
Т.е. я не могу написать код переименования файликов и распространить его между коллег. Мне надо пойти к безопасникам, долго и упорно просить выдать мне сертификат для подписи кода и, возможно, если соответствующая инфраструктура есть в организации, мне его дадут.
...или надо распространить код в текстовом виде и просить коллег сделать Ctrl+C, Ctrl+V в *.ps1.
По сравнению с bash'ем выглядит как создание проблем на ровном месте.
ildarz
00.00.0000 00:00+5Если у вас "корпоративные безопасники" не понимают, что делают - вряд ли это проблема powershell.
NAI
00.00.0000 00:00Безопасники люди простые - чем меньше разрешено, тем меньше риски. Т.е. проблемы индейцев шерифа не волнуют. И вот дальше ваша задача объяснить что эта штука ооочень нужна, она безопасна и все такое прочее.
dartraiden
00.00.0000 00:00+1Кто мешает проставить их же злоумышленникам — вообще не понятно.
Это защита от неопытного пользователя:
ExecutionPolicy is like a baby door. The ExecutionPolicy keeps babies safe but every grown-up surpasses it easily.
There are like over 20 ways to surpass the ExecutionPolicy even as a standard user.надо или прописывать какие-то непонятные заклинания в реестр, или ставить не менее непонятные ключи при вызове.
- Good God! — сказал Ойра-Ойра, протирая запорошенные глаза. — Canst thou not come in by usually way as decent people do? Sir… <Ужель обычный путь тебе заказан — путь достойного человека? Сэр… — (англ.)> — добавил он.
alexei_ovsyannikov
00.00.0000 00:00+2Зачем PowerShell сравнивать с Python с точки зрения функциональности? Это же совершенно разные инструменты...
roqin
00.00.0000 00:00+1Кстати, Python скрипт везде стартует быстрее. PowerShell тупит доли секунды. Обычно это неважно, но когда на любое действие GUI вызывается скрипт то разница чувствуется.
Да у меня на винде
powershell.exe
запускается долго и жрёт много памяти (по сравнению сbash
). Что он там делает - без понятия. Загадка.iSeiryu
00.00.0000 00:00+1Тут надо версию powershell указать и антивирус, который каждый запуск powershell сканирует.
Powershell медленный.
Pwsh (core который) быстрый - на моём i7 загрузился, вместе с моим кастомным профилем с темой, модулями, функциями за 853 миллисекунды и кушает 30мб памяти. Без всех моих обвесов должно быть меньше.
Tzimie Автор
00.00.0000 00:00А можно сравнить с Python? Спасибо
iSeiryu
00.00.0000 00:00Для этого надо питон нативно в систему добавлять, мне боязно. Если таки решусь, то отпишусь.
Он у меня есть в контейнере и в WSL. Но через эти инструменты нормально загрузку терминала не проверить.
iSeiryu
00.00.0000 00:00+1Поставил Python 3.11 из Microsoft Store и добавил его в профиль в Windows Terminal:
\AppData\Local\Microsoft\WindowsApps\python.exe
Он загружается тоже где-то за 10мс и кушает 8мб.
Tzimie Автор
00.00.0000 00:00+2P.S. мне не кажется, что 0.8s это быстро
iSeiryu
00.00.0000 00:00Убрал все мои обвесы и обе версии powershell загрузились за 10мс.
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe C:\Program Files\WindowsApps\Microsoft.PowerShell_7.3.3.0_x64__8wekyb3d8bbwe\pwsh.exe
Старый powershell кушает 25мб памяти.
Новый pwsh кушает 16мб.
Kenya-West
00.00.0000 00:00Ждём от вас
ссылку настатью, в которой объясняется, как эти красивости сделатьiSeiryu
00.00.0000 00:00+1Прочитал как "кривости" и минуту не мог понять, о каких кривостях речь.
Для красоты используется вот это https://ohmyposh.dev/
Для функционала вот эти модули:
https://github.com/PowerShell/PSReadLine
https://github.com/dahlbyk/posh-gitПрочие ссылки:
https://www.youtube.com/watch?v=n1sFkbPlDww
https://www.hanselman.com/blog/my-ultimate-powershell-prompt-with-oh-my-posh-and-the-windows-terminalАвтодополнение - крутая штука. Из истории по умолчанию и можно добавить разных умных дополнений с подсказками для git, docker, kubectl, az, aws, и прочее.
. .
mvv-rus
00.00.0000 00:00+13Вы не любите Powershell? Да вы просто не умееете его готовить! ;-)
Вам не нравится расхлябанность с неописанными переменными? Включите строгий режим (Set-StrictMode -Version Latest).
Вам не нравится, что Powershell по-разному работает с последовательностями из 0, 1 и нескольких элементов? Используйте операцию @(...)
Вам не нравится, что Powershell передает результаты функций через конвейер? Да, отучить его от этого нельзя — потому что он все-таки shell. Идеология у него такая. Но к этому можно приноровиться — просто привыкните сразу выбрасывать все лишнее: ставьте [Void] перед вызовами внешних команд, функций и в прочих местах, где значение результата надо проигнорировать.
И тогда вы наверняка перестанете бояться и полюбите Powershell. Потому что он позволяет ещё много чего. В частности — с легкостью пользоваться практически всей библиотекой времени выполнения .NET (мне, как человеку, хорошо знакомому с C#, это очень полезно).
PS Лично я полюбил Powershell, потому что много работал с MS Exchange, а там, начиная с Exch2K7, альтернатив просто не было.PPS Вы, AFAIK давно работаете с MS SQL. Не поделетесь, чем вы пользовались для написания скриптов до появления в нем Powershell? vbscript/jscript+ADO? Или — isql/osql с последующим мучением разбора текстового вывода средствами cmd? Это я чисто для себя интересуюсь — потому что я в те давние времена этот вопрос для себя так и не решил.
Tzimie Автор
00.00.0000 00:00Сейчас я использую PowerShell, потому что так принято в фирме, где сейчас работаю. Вроде 1000 строк, скоро будет статья)
Если бы было всё равно, то писал бы на Питоне
До PowerShell я все писал на SQL, вызовешь команду в xp_cmdshell и парзишь вывод там же)
ildarz
00.00.0000 00:00-1Языки программирования РАЗНЫЕ. И особенности каждого из них надо изучать, а не пытаться действовать по кальке с другого языка. Описанные вами "неожиданности" PS либо прямо следуют из логики происходящего, либо просто явным образом описаны в документации (про ваш пример с return там вообще почти цитата в about_return есть).
Что до неявного преобразования типов - так в той области, где PS обычно используется, это однозначное благо, сильно упрощающее жизнь. А там, где оно вдруг работает не так, как ожидается, всегда можно сделать явное.
mc2
00.00.0000 00:00+4Откроем wiki на каждый из "языков" и осилим первое предложение:
Python is a high-level, general-purpose programming language.
PowerShell is a task automation and configuration management program from Microsoft, consisting of a command-line shell and the associated scripting language.Так что сравнение теплого с мягким.
DikSoft
00.00.0000 00:00+4Из всех "неприятностей" реально утомляет в скриптах только одна: проверка, что за тип нам вернули. $null , объект или массив.
Остальное, как мне кажется, надумано или просто противоречит старым привычкам.
Плюсы "всё объект" и "всё можно отправить в конвейер" перевешивают минусы. Не надо парсить строки, где у каждого автора "утилиты, которая делает только одно, но отлично" своё, блин, представлние о прекрасном. И где простое True может быть написано десятком разных и порой весьма извращённых вариантов.
Daysleeper
00.00.0000 00:00+1Давно работаю с PowerShell и недавно начал осваивать python (без особого энтузиазма). PowerShell прекрасный инструмент и мне очень нравиться. Но у меня нет желания написать статью с кликбейтным названием "ужасы python", только потому что я плохо знаю python. Почему автор решил устроить холивар, для меня осталось загадкой.
HemulGM
Сравнивать PS с Питоном - очень странное занятие
Здесь мне нечего сказать. Переменная есть? Есть. Значение в ней, как во многих языках программирования может быть стандартное (по умолчанию). Мы можем объявить строку и вывести её. Компилятор может даже не ругаться. А здесь переменная объявляется в момент её непосредственного вызова. PS. в js тогда вообще лучше не лезьте
Тип "переменной" определяется содержимым файла! Вот и всё объяснение твоего недоумения. На питоне вы грузите содержимое совершенно иначе и если сделать так же на PS результат будет как и в питоне
И не только файла, но и данными, полученными откуда-то ещё.
А забрать значение из функции не пробовали? Ну там, в переменную и вывести потом? При чем тут результат функции, если вы просто выводите значение на экран?
Аналогичный код на псевдо языке:
Tzimie Автор
Так оно и забирается в переменную $res
HemulGM
https://learn.microsoft.com/ru-ru/powershell/module/microsoft.powershell.core/about/about_return?view=powershell-7.3