Решил я переехать со своего славного города в столицу. Дела почти все закрыл: Уволился с работы. И что-то мне огонька не хватает видимо, решил план переезда очень плотно так уложить в два выходных, при этом съехав с квартиры, которую арендовал в своем городе.
В голове план был надежный, как швейцарские часы:
Пятница
18:00
Последний рабочий день, я окрыленный свободой и предвкушением чудесного путешествия в Москву на машине, мчу на арендную квартиру, с которой нужно вывезти остатки вещей, чтобы сдать хозяйке.
20:00
Сдаю квартиру - еду на временную квартиру переночевать.
Суббота
6:00
Выехал из своего прекрасного города.
21:00
Приехал в Москву.
Звучит надёжно.
Как оно произошло на самом деле
Пятница.
Заканчиваю рабочий день в 18:30. Собираю вещи к сдаче квартиры арендодателю - все вещи конечно в мой маленький хэтч не поместились - больше половины вещей пришлось грузить в свеже-заказанную газель.
По итогу: 2:00 ночи, ложусь спать, чтобы в 7:00 утра встать и поехать в МСК, так как в понедельник утром меня уже ждут выходить на новом месте работы.
Суббота.
Старт поездки был прямо таки неплох, ничего не предвещало. Я успешно боролся с залипанием глаз до 11 утра, затем до 16:00 достаточно скучно проехал все участки дороги.
Интересное началось в 18:30. Проехал Воронеж, 60км уже как, хотя по внутреннему вроде всего ничего прошло. Заехали на очередной виток платной дороги. Кстати о ней, не совсем понятно за что там деньги берут - ямы в крайнем правом ряду иногда такие, что и камазик спокойно на боковую может отправится.
Так вот, еду я, значит, в левом ряду - никого не трогаю. По платной, япона мать, дороге. Меня догоняет сзади какой-то прыткий мальчуган и просит подвинуться в крайний правый. Я правила знаю - перестраиваюсь и при перестроении пропускаю крайне, КРАЙНЕ неприятную мелкую, но очень глубокую яму, которую конечно же ловлю левым передним колесом.
Вслух выражаю некоторую обеспокоенность сложившейся ситуацией, так как понимаю, что колесу скорее всего пришел боковой разрыв. Но надежда была первые секунды две, потом я в руле ощутил давление в левую сторону и решил останавливаться.
Промежуточный статус.
Тааак. Суббота. Время 18:30. 60км от Воронежа. Мне в понедельник железно необходимо быть в Москве на новом месте работы. Запаски \ докатки нет. Колеса - 225\40 R18 Bridge-матьих-stone Blizzak LM005. В голове родился план и оперативно начал его выполнение.
Вызвать эвакуатор.
Найти рабочую \ хорошую шиномонтажку.
Понять что с колесом \ возможно потребуется еще одна покрышка \ найти где купить.
Эвакуатор все равно вызывать - оперативно вызываю с Воронежа с 5-го номера по авито, меня ориентируют, что в течение часа заберут - "ок, по рукам". Ждем-с.
Пока жду, ищу шиномонтаж 24 часа - телефон никто не берет, те кто берут - уже домой собираются, либо к времени моего приезда с машиной уже не будут работать. 10 звонков - все в холостую. Ладно, фиг с ними, надо переключиться - пошли в п.3.
Выхожу из машины, думаю: варианта два, но более очевидный - боковой разрыв. Осматриваю бочок покрышки передней. Да, так и есть - трещина в 5 сантиметров. Что-ж, будем искать колесо.
Понеслась, 2gis \ yandex maps -> Продажа шин. Все закрыто, либо ни у кого ничего нет нужного мне колеса. И тут, о чудо, нашел магаз с одной нужной мне покрышкой. Звоню, на проводе Светлана, мы с ней договариваемся на покупку нужного мне колеса. Я прошу ее замутить схему с отправкой шины на такси через часок другой. Покрышка стоит правда 18к (ни себе чего, подумал я, но шо поделать). Оплачиваю шину, жду эвакуатор. Пока жду - приезжает какой-то левый эвакуатор и говорит, что может докинуть меня за недорого, почти бесплатно (всего то 4к) до ближайшего села. Я оценив перспективы, крайне нулевые, решил отказаться.
Проходит еще минут 30, пока заказанный эвакуатор приезжает, забирает меня и начинает вселять в меня очень много оптимизма своими тезисами: "да х*й ты тут, конечно, у нас такое колесо найдешь", "да и шиномонтажку тоже в нашем селе сложно найти, но я знаю одну хорошую, туда и поедем". Пока едем, терзающие мою голову сомнения пробиваются наружу и я решаю проверить, где я собственно шину купил то. Прозванию Светлане - уточняю, а в каком собственно, она городе мне шину отгрузить планирует. Услышав Москву, вместо Воронежа, немножечко взгрустнулось, но не время паниковать. Договариваемся на возврат денег. Квест "Найти 225\40 R18" продолжается.
Пока едем в эвакуаторе, открываю авито в Воронеже, нахожу на первой же странице продажу каких то китайских 225\40 R18 шин немного Б\У за 3100 деревянных. "Заебумба", думаю я. Есть все шансы попасть в МСК сегодня. Край завтра утром. Договариваюсь с продавцом на покупку, благо, как оказалось, он живет рядом с шиномонтажкой в которую меня везут и он мне привезет эти покрышки.
Так, можно немного выдохнуть. Приезжаем на шиномонтажку, парни снимают оба колеса по левой стороне, говорят, диску немного поплохело, надо бы поправить, да и задний диск тоже не в лучшей форме. Фиг с ним, делаем, шины привезут в течение получаса.
Пока нам везут колеса, зашли поели. Приехал продавец колес на Субарике, отдал колеса, получил 3100 на лапу, разбежались.
Прошло еще полчаса - получаю машину, способную на свершения. Заезжаю на заправку, лью до полного. Время 22:45. До Москвы 6 часов, я оптимистично смотрю в будущее, выезжаю из Воронежа в надежде приехать в Москву к 4:50 утра....
Вводишь номер объекта в скрипт, он ищет на нём ПК в Active Directory и выдаёт информацию по количеству свободного места на диске и размеру диска. Выделенная область не совсем понятна. Знак доллара, собака, вот это вот всё. Если есть возможность разжевать цикл для тупых) и как это склеивается с командлетом.
Write-Host "ERROR!" $PCs[$i].Name $_ -ForegroundColor Red
}
}
Clear-Variable -Name "PCs"
}else{
Write-Warning "The entered value is incorrect"
}
Немного теории про переменные:
Переменная — это единица памяти, в которой хранятся значения. Рекомендуется, чтобы имена переменных включали только буквенно-цифровые символы и символ подчеркивания (_). Имена переменных, включающие пробелы и другие специальные символы, трудно использовать и следует избегать.
В PowerShell существует несколько различных типов переменных.
Созданные пользователем переменные: созданные пользователем переменные создаются и поддерживаются пользователем. По умолчанию переменные, созданные в командной строке PowerShell, существуют только во время открытия окна PowerShell. При закрытии окон PowerShell переменные удаляются. Чтобы сохранить переменную, можно добавить ее в профиль PowerShell. Можно также создавать переменные в скриптах с глобальными, скриптами или локальными область.
Автоматические переменные: автоматические переменные хранят состояние PowerShell. Эти переменные создаются PowerShell, и PowerShell изменяет их значения в соответствии с требованиями, чтобы обеспечить их точность. Пользователи не могут изменить значение этих переменных. Например, $PSHOME переменная сохраняет путь к каталогу установки PowerShell.
Переменные предпочтения: переменные предпочтения хранят пользовательские настройки для PowerShell. Эти переменные создаются PowerShell и заполняются значениями по умолчанию. Пользователи могут изменять значения этих переменных. Например, $MaximumHistoryCount переменная определяет максимальное количество записей в журнале сеансов.
Теперь с картинками:
Переменные в powershell начинаются со знака "$". Имена переменных не зависят от регистра, то есть переменные $nameVar и $NAMEvar - одна и та же переменная. Присвоение значения переменной происходит через оператор "=".
Имена переменных могут включать пробелы и специальные символы. Например: ${name of variable}. Использование такой вариации имени сомнительно, но поддерживается.
После присвоения значения переменная будет иметь свой тип. У всех типов переменных есть общие и уникальные свойства и методы, к которым можно обращаться через точку. Для того, чтобы узнать какие методы и свойства есть у текущей переменной, можно воспользоваться командой Get-Member, как в примере ниже.
Тип переменной $name - System.String, то есть строковый.
Для работы с массивами объектов в переменных 2 очень важных свойства:
Length (длина) и Count (количество). Зачастую данные свойства используют для определения элементов в массиве, но часто ошибочно используют Length, когда желательно использовать Count. Дело в том, что свойство Length действительно в большинстве сценариев отображает количество элементов массива, кроме случая, когда в массиве один элемент строкового типа. В этом случае свойство Length выдаст количество символов в строке.
Примеры получения этих свойств ниже:
Разница в значении свойств Length и Count проявляется в примере переменной $var_text. Свойство Length отобразило количество символов в строке (длину). Свойство Count отобразило отобразило количество строк (или объектов в массиве). Поэтому для корректности крайне рекомендуется использовать свойство Count для определения количества элементов массива.
Анализ
Первая проблема данного кода - обильное использование алиасов, не прозрачных конструкций и отсутствие комментариев. Попробуем разобраться, попутно изменив код на более читаемый и оставив комментарии.
Начнём с того, что приведем скрипт в читаемый вид, выровняв основные блоки кода:
Отформатированный в читаемый вид изначальный текст скрипта.
На вход описанному блоку скрипта должна прийти переменная $SearchBase со значением отличным от символа "0". Открывает выполнение кода условный оператор if () else. Условие проверки странное, так как минимально необходимая обычно проверка - это проверка на пустоту какой-либо необходимой для выполнения блока кода переменной, но для этого достаточно оставить в проверке if переменную без операторов сравнения. Здесь же у нас if получит TRUE в случае, если $SearchBase не будет эквивалентен символу "0".
Учитывая, что нам не известно какие значения $SearchBase может принимать, менять логику проверки не стоит. С учетом анализа дальнейшего использования данной переменной, становится ясно, что она используется для определения области в домене Active Directory, из которой будут выбираться объекты компьютеров в командлете Get-ADComputer
Исходя из этого (и из примеров использования Get-ADComputer), переменная $SearchBase должна содержать строку DN (Distinguished Name) контейнера Active Directory в формате
Допустим, в переменной $SearchBase у нас все таки что-то похожее на Distinguished Name контейнера в AD (либо корень домена - "DC=domainName,DC=local, такое определение области тоже имеет место быть). Идем дальше: алиас select меняем на полное имя командлета Select-Object
Здесь у нас происходит следующее: сначала получаем все объекты компьютеров из AD по указанному контейнеру в переменной $SearchBase.
Затем по конвейеру (символ | ) результаты выполнения этой команды передаются на команду Select-Object:
Командлет Select-Object выбирает указанные свойства объекта или набора объектов. В нашем случае из свойств объектов компьютеров Active Directory выбирается только свойство Name. Набор свойств по умолчанию, доступных в объекте компьютера, возвращаемый командлетом Get-ADComputer представлен ниже.
Затем все имена компьютеров сохраняются в переменную $PCs. То есть, если после оператора присвоения переменной (символ =) происходит выполнение нескольких командлетов с передачей результатов по конвейеру, в переменную будет сохранен результат выполнения последнего командлета (в нашем случае это Select-Object).
Таким образом, в переменную $PCs (при корректной передаче $SearchBase, естественно) будет сохранен массив свойства Name объектов компьютеров из AD. Примерно следующего вида:
Далее идет цикл For для обработки элементов массива переменной $PCs.
В условиях цикла For стоит перебор по одному элементу массива $PCs, начиная c нулевого ($i=0; $i -lt $PCs.Length; $i++). Условием остановки цикла является перечисление всех объектов из переменной. Для решения данной задачи немного проще использовать цикл Foreach, но про него поговорим в следующей статье. Единственное исправим $PCs.Length на $PCs.Count
Затем, для каждого элемента массива выполняется конструкция Try {} Catch {}.
Блок Try {} будет выполняться для каждого элемента массива, блок Catch {} будет выполнен только в случае, если в блоке Try будет зарегистрирована исключительная ситуация (ошибка при выполнении командлета, например).
В блоке Try {} выполняется три командлета:
получение информации о Wmi-объекте win32_volume (раздел диска) с удаленного компьютера (командлет Get-WmiObject). Первый параметр командлета Get-WmiObject -Class является позиционным, и его можно не указывать, но такая практика не является рекомендуемой, так как это усложняет чтение кода. Второй параметр -ComputerName определяет имя сервера \ рабочей станции из массива переменной $PCs. Так как в переменной $PCs у нас хранится массив свойств Name, то к этому свойству через символ точки и обращаемся, при этом указав порядковый номер элемента с помощью квадратных скобок. То есть, в записи $PCs[$i].Name - $PCs это весь массив свойств имен компьютеров из Active Directory. В квадратных скобках указан текущий номер элемента массива в цикле For, а если это первая итерация, то там у нас будет 0. И через символ точки "." мы получаем имя компьютера для передачи в параметр -ComputerName. Параметр -ErrorAction определяет поведение консоли при обработке вероятной ошибки (исключительной ситуации, например компьютер целевой не доступен и не удается получить информацию), при выполнении данного командлета.
Значение параметра -ErrorAction Stop означает, что в случае исключительной ситуации, дальнейшее выполнение блока Try {} будет прекращено в рамках данного прохода цикла For и будет выполнен код из блока Catch {}, который как раз и служит для отработки кода в исключительных ситуациях.
Для того, чтобы понимать происходящее в коде, крайне важно понимать с каким объектом (и какие у него свойства) в данный момент происходит действо.
Для примера, разберем какого типа возвращает объекты командлет Get-WmiObject. Для этого попробуем выполнить следующее:
Таким образом, можно увидеть какие свойства и какие значения этих свойств есть у объекта типа win32_volume. Вывод свойств по умолчанию у объектов разный, и зачастую избыточен, поэтому после того, как вы поняли какие свойства вас интересуют, полезно будет научиться использовать следующие командлеты:
Select-Object - командлет, используемый для отображения только указанных свойств переданных по конвейеру объектов:
Where-Object - командлет, используемый для фильтрации массива объектов по условному свойству (или совокупности свойств). Рассмотрим пример ниже с массивом объектов-служб в переменной $services. Допустим, нам необходимо отфильтровать и получить только сервисы в переменную $targetService которые имеют в DisplayName слово "Update" и у которых статус "Running":
Фигурные скобки с условиями для командлета Where-Object практически всегда используются, поэтому просто привыкните пока использовать данный командлет в таком режиме. Из нового здесь у нас использование переменной $_ - что это такое и зачем она нужна.
Если вкратце, то $_ это алиас для автоматической переменной $PSItem PowerShell, используемый в скриптблоках, обрабатывающих текущий объект, например в конвейере. То есть $_ хранит в себе текущий объект, переданный по конвейеру.
Вернемся к нашим баранам
Далее по скрипту передача по конвейеру Wmi-объекта на командлет Where-Object с целью фильтрации. Условием для фильтрации является эквивалентность свойства Name WMI-объекта значению "C:\". То есть, фильтр выставлен, чтобы дальше по конвейеру пошли только объекты, которые являются логическим диском C:\.
Затем используется уже известный командлет Select-Object, но с интересным способом изменения отображаемых полей свойств приходящего WMI-объекта при отображении:
свойство __SERVER меняется ComputerName
свойство Capacity останется в таком же виде, но значение его будет изменено согласно формуле $_.Capacity/1GB.
свойство FreeSpace останется в таком же виде, но значение будет изменено согласно формуле $_.FreeSpace/1GB.
Также будет выведено свойство Name без изменений
Касательно изменений значений объема и свободного - $_Freespace / 1GB.
Такого рода преобразование используется в случаях когда значения свойств хранятся с байтах (а это наш случай), а мы хотим вывести значение в гигабайтах.
Затем идет блок кода Catch, в котором отработает командлет Write-Host - который красным цветом напишет в консоль "ERROR!", имя текущего сервера, по которому не удалось отработать блок Try. Далее, в конце каждой итерации сработает командлет очистки переменной $PCs. Затем описано блок Else, скриптблок которого отработает в случае, если условие $SearchBase -ne "0" не будет выполнено. Скриптблок содержит один командлет, который напишет текст "The entered value is incorrect" в консоль.
Конечный, немного поправленный блок скрипта выложу картинкой
На дворе Апрель месяц 2003 год. Птички поют, кукушки кукуют. Microsoft выпускает в свет свою прекрасную Windows Server 2003. Системные администраторы в слезах счастья рассказывают любителям консоли как много изменений в AD, в безопасности, в *any_windows_feature_name* произошло и как теперь здорово пользоваться окнами.
Одуревший от счастья нажиматель кнопок на клавиатуре.
И всё бы хорошо, но любители пингвинов стали активнее намекать жалким любителям GUI о том, что консоль это божественно, а кнопки мышью нажимать - удел ламеров. Bash, мол, позволяет скрипты писать и вообще делать всё что душе угодно в плане автоматизации.
Конечно, в Windows есть cmd и даже есть возможность писать bat-скрипты, но выглядит енто, конечно убого. Глобальных отличий непосредственно в самих оболочках со стороны конечного пользователя черного экрана не так много, но дьявол скрыт в мелочах.
Для начала посмотрим, что же енто за cmd \ bash такие и зачем они собственно нужны.
command line interpreter (CMD)
Типичный вид cmd.exe
CMD — это командная строка для операционной системы Microsoft Windows с функциями, основанными на командах. Конвейера нет, вывод результатов в строке. Долго отсутствовали инструменты для работы с текстом.
Bourne again shell (Bash)
Типичный вид bash
Bash — это язык командной строки и сценариев для большинства операционных систем на базе Unix/Linux. Есть конвейер, вывод результатов в строке. Имеются инструменты для работы с выводом в виде текста (grep \ awk \ cat \ tail и т.д.). Они позволяют отделить нужные буквы из строки от ненужных при выводе результатов действия команд.
Шли годы, nix-админы наращивали скилл в умении прерарировать текст, оконные админы жмакали кнопки в gui и плакали при написании bat-скриптов, попутно совместно меряясь писюнами, кому же из них работать сложнее. И так бы оно и было, пока ребята из Microsoft не решили выкатить его. Его величество Powershell.
- В чем же величие? - спросите вы.
Powershell это такая же среда выполнения скриптов и взаимодействия с OS, как и cmd \ bash. Есть одно но - powershell умеет в объекты. Он по умолчанию работает с объектами и их свойствами. Почему это так важно? Потому что вывод в строки большинства утилит и приложений, с которыми умеет работать cmd \ bash не имеют единого стандарта вывода. ipconfig может выдать вам совершенно разное полотно информации на разных OS, при разных количествах сетевых интерфейсов и просто если луна не в меркурии будет.
Вывод текста утилитой ipconfig
Допустим вы захотели вытащить через cmd IP-адрес.
На Bash сделать это достаточно легко. Для этого достаточно использовать простую команду ниже:
ifconfig eth0 | sed -n '2 {s/^.*inet addr:\([0-9.]*\) .*/\1/;p}'
Сильно, правда? Всего regexp (регулярные выражения) надо знать, как свои 9 пальцев.
В Windows тогда вообще с этим дела никак не обстояли, поэтому приходилось изучать полотно текста и радоваться, что вообще хоть какая-то информация в консоли отображается.
С приходом powershell консоль научилась работать с объектами и при вводе командлета (название стрёмное, да, так уж они там называются): Get-NetIPAddress объекты, у которых есть одинаковые свойства, к которым можно обращаться.
История с объектным ориентированием консоли сильно облегчила возможность получения информации о системе. Например, теперь чтобы получить информацию об IP адресе, нет нужды изучать regexp, а достаточно запомнить, что к свойствам в объектах можно получить доступ через точку:
Помимо этого powershell поддерживает конвейер для передачи результатов деятельности из одной команды в другую, if - else \ switch ветвления, циклы do while \ until, создание функций и своих объектов.
В первой версии Powershell 1.0 функционал был немного куцый в сравнении с текущей версией, но его объектная ориентированность уже принесла много счастья в руки любителей автоматизировать.
Вторая версия добавила возможность удаленного выполнения кода, поддержку Windows Server 2008 и Windows 7. Помимо этого большинство программных продуктов Microsoft не только нативно поддерживало работу через powershell, но и предоставляло больше возможностей, нежели GUI.
Powershell 3.0 появился рядом с Windows 2012 и Windows 8, получил еще большее развитие командлетов и возможностей отладки. В общем так и продолжалось его развитие вплоть до 5ой версии, на которой Microsoft решили, что нужно сделать cross-платформенную оболочку и объявили о создании PowerShell Core c поддержкой различных систем в названии которых отсутствует слово Windows: MacOS \ Ubuntu \ RHEL \ CentOS и так далее.
Какие еще возможности предоставляет эта оболочка я расскажу в следующей статье Cat's & Lamp.