Сегодня хочу поговорить про использование алиасов в скриптах и дать несколько практических советов при сборке текстов из фрагментов.
Семантика языка PowerShell подразумевает использование унифицированного синтаксиса, где название каждой команды явно говорит о том, что она делает. Например, Get-Process. Совершенно очевидно, что эта команда должна делать. Но, порой, эти команды бывают очень длинными и набирать их постоянно в консоли бывает не очень удобно. Например, самый топовый — Get-ChildItem. Это даже не самая длинная команда, просто наиболее часто используемая. Или Foreach-Object. Даже автозавершение команд не всегда спасает ситуацию. Для этого были придуманы алиасы (короткие ссылки на команды), которые очень выгодно использовать в консоли. Так же у команд есть и очень длинные параметры. Например, всякие –InputObject, –Include, –ErrorAction и т.д. PowerShell позволяет сокращать параметры первыми буквами до тех пор, пока эти буквы не будут явно указывать на конкретное название. Например, у команды Get-ChildItem параметр –Include может быть сокращён до –I, а –Exclude до –Ex. Но многие скриптописатели пишут скрипты (и выкладывают их даже где-то) с использованием этих самых алиасов и коротких обозначений параметров.
Хорошо это или плохо? Ответ очевидный — за такое надо бить больно сапогами и по лицу. Использование алиасов приводит нас обратно к одной из проблем оболочки cmd — сразу не скажешь, что делает та или иная команда. Пользователь без соответствующей подготовки вряд ли сходу скажет, что делает команда regsvr32 или что делает ключ /i этой команды. Или вот 2 примера:
gps iex* | spps -f ls .\ -r -fo | %{cp $_.fullname -des e:\ -ea 0}
Вы можете такое использовать в консоли, но не в скриптах. В скриптах эти две строчки должны выглядить только вот так:
Get-Process iex* | Stop-Process -Force Get-ChildItem .\ -Recurse -Force | ForEach-Object {Copy-Item $_.fullname -Destination e:\ -ErrorAction SilentlyContinue}
вот такое написание значительно повышает читабельность кода и можно понять его работу даже без выполнения, а просто на стадии чтения и, если они есть, обнаружить какие-то ошибки. Да и вы сами со временем можете забыть, что это был за алиас и на что он ссылается. Уважайте себя и других.
Пользователи PowerGUI Script Editor могут воспользовться адд-оном, написанным одним пошикмвп Шейем Леви (Shay Levy) — Expand Alias, который автоматически разворачивает алиасы в их полное значение.
PowerShell очень часто используют не только для созидательных целей, но и для аналитических тоже — сбор каких-то данных. Или какой-то скрипт выполняющий сложные операции и попутно пишущий что-то в лог-файл.
На этой неделе мне прислали скрипт, который на выходе делает красивую HTML'ку с красивыми стилями и всё такое. И вот какое чудо (не единственное) я увидел:
$a = "<title>Телефонный справочник ООО `"Имя компании`"</title>" # стили и заголовок окна $a = $a + "<style>" $a = $a + "BODY{background-color:#FFF;color:#000;font-family: tahoma; font-size: 8pt; }" $a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: #BFBFBF;border-collapse: collapse; width: 100%}" $a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: #BFBFBF;background-color:#4E7DD1; color: #FFF}" $a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: #BFBFBF;background-color:#FFF}" $a = $a + "col#c1 { width: 10%;} col#c2{ width: 10%; }col#c3 {width: 10%;}col#c4 { width: 3%;}" $a = $a + "col#c5 { width: 10%;} col#c6{ width: 11%; }col#c7 {width: 10%;}col#c8 { width: 25%;}" $a = $a + "</style>"
Ах, какая красота. Уже в первой строке человек использует бэктик (`) для эскейпа двойных кавычек внутри текстовой строки. Зачем? Я не знаю. Ведь достаточно было заменить наружные кавычки на одинарные и всё стало бы прекрасно:
$a = '<title>Телефонный справочник ООО "Имя компании"</title>'
А какую функцию несёт переменная $a? Мы уже говорили, что название переменной должно отражать её сущность. Это сейчас понятно, что там, а чуть ниже в коде, где она будет использоваться — мы уже забудем, что там было.
Ок, идём дальше. А дальше он добавляет к этой строке другой код HTML (естественно, в виде текста). Я давно замечал, что люди или не знают или не хотят использовать here strings. Ведь можно сделать вот так:
$html = @' <title>Телефонный справочник ООО "Имя компании"</title> <style> BODY{background-color:#FFF;color:#000;font-family: tahoma; font-size: 8pt; } TABLE{border-width: 1px;border-style: solid;border-color: #BFBFBF;border-collapse: collapse; width: 100%} TH{border-width: 1px;padding: 0px;border-style: solid;border-color: #BFBFBF;background-color:#4E7DD1; color: #FFF} TD{border-width: 1px;padding: 0px;border-style: solid;border-color: #BFBFBF;background-color:#FFF} col#c1 { width: 10%;} col#c2{ width: 10%; }col#c3 {width: 10%;}col#c4 { width: 3%;} col#c5 { width: 10%;} col#c6{ width: 11%; }col#c7 {width: 10%;}col#c8 { width: 25%;} </style> '@
Если вам надо в коде разместить набор строк, заключайте их в here strings (с двойными кавычками, если внутри надо экспандить переменные или с одинарными, если это не надо).
Так же, в этом скрипте увидел ещё вот такую прелесть:
$menu = $menu + "<a href=`"#"+$head+"`">"+$head+"</a><br>"
А если мы снова упакуем это в here strings?
$menu += @" <a href="#$head">$head</a><br> "@
и мы снова избавились от груды бесполезных кавычек и необходимости их эскейпить.
Вот ещё прелесть:
$PatternTo = "<col id=""c1""/><col id=""c2""/><col id=""c3""/><col id=""c4""/><col id=""c5""/><col id=""c6""/><col id=""c7""/><col id=""c8""/>"
И здесь мы видим груду бесполезных кавычек. А давайте это подмножество строк положим в одну строку, заключённую в одинарные кавычки:
$PatternTo = '<col id="c1"/><col id="c2"/><col id="c3"/><col id="c4"/><col id="c5"/><col id="c6"/><col id="c7"/><col id="c8"/>'
эффект тот же самый и минус 16 кавычек.
Отсюда следует вот такое правило: учитесь правильно использовать кавычки. Избегайте эскейпинг кавычек — в 99% случаев это не нужно совсем. Вместо этого надо предусмотреть использование одинарных кавычек или применять here strings, если выхода нет.
$menu= "</style>" + "<TABLE Style=`"border-color:#FFF; border-collapse: separate`"><tr><td Style=`"border-color:#FFF`">" +$menu + "</td></tr></TABLE>"
Например, здесь мы не можем обойтись просто одинарными кавычками, потому что внутри строки надо вставить значение переменной. Значит, нам поможет here strings:
$menu= @" </style><TABLE Style="border-color:#FFF; border-collapse: separate"><tr><td Style="border-color:#FFF">$menu</td></tr></TABLE> "@
В PowerShell при работе с большим объёмом текста (уже начиная от мегабайта) наблюдается ощутимая деградация производительности. Например, ваш код на выходе генерирует большой XML или HTML код, следует избегать операторы конкатенации ($a = $a + "some text" или $a += "some text"). Вместо этого следует использовать StringBuilder:
# создаём объект StringBuilder $SB = New-Object Text.StringBuilder # добавляем фрагменты текста в коде <...> [void]$SB.Append("some string") <...> # выгружаем итоговый текст в файл: Set-Content -Path $path -Value $SB.ToString()
В качестве примера можете посмотреть мой код в PSFCIV, который заворачивает объекты с конвейера в XML:
function _toxml_ { # в Begin создаём заголовок XML и открывающийся тег <FCIV> Begin { $xmlstring = New-Object System.Text.StringBuilder [void]$xmlstring.Append('<?xml version="1.0" ?><FCIV>') } # в Process будут по одному поступать объекты, которые описывают файл. Process { # для каждого файла у нас будет один тег <FILE_ENTRY> [void]$xmlstring.Append(" <FILE_ENTRY>`n") # чтобы вручную не создавать и не заполнять вложенные теги мы простым foreach # перечисляем теги, какие у нас будут в XML и за счёт переменных автоматом # создаём их в XML и заполняем их данными. Для этого свойства объектов должны # называться так же, как и теги. foreach ($child in ("name", "Size", "TimeStamp", "SHA1", "MD5")) { [void]$xmlstring.Append(" <$child>$($_.$child)</$child>`n") } # когда текущий объект обработан, закрываем тег и ждём следующий объект файла [void]$xmlstring.Append(" </FILE_ENTRY>`n") } # когда объекты закончились, закрываем первый тег <FCIV> и подаём полученный XML дальше End { [void]$xmlstring.Append("</FCIV>`n") [string]$xmlstring.ToString() } }
В этом случае деградация производительности будет минимальной.
Пошик совсем деформирует мозг. Не так надо: Get-Process iex* А вот так: Get-Process -Name "iex*" Ну и всё остальное в таком же духе.
Да, наверное, так совсем правильно.
Алиасами вообще лучше не пользоваться или пользоваться стандартными, которые есть во всех пошах. А то понаделают люди себе их, потом привыкнут, сядут в новую среду и ... опа.. начинается. А руки ко всему привыкают, команды длинные быстро набираются, когда есть TAB
И кстати фотку поменяй, ты уже совсем не такой гламурный.
> или пользоваться стандартными, которые есть во всех пошах нет, ими тоже не пользоваться в скриптах. Считайте за правило, что алиасами следует пользоваться только в интерактивной консоли. > И кстати фотку поменяй, ты уже совсем не такой гламурный. у меня нету другой нормальной фотки.
Comments: