Archive for the ‘PowerShell’ Category

Выкачивание драйверов с сайта HP

воскресенье, мая 9, 2010

usbVacuum Не так давно мне понадобилось скачать драйверы и утилиты для множества моделей компьютеров HP. Учитывая что обычно для каждой модели приводится несколько десятков драйверов, скачивать их вручную и раскладывать по папкам мне показалось слишком долго и нудно. Кроме того я не хотел устанавливать какие либо менеджеры закачек которые помогли бы просто выдрать все ссылки со страницы, да они и не смогли бы правильно разложить файлы по категориям… Поэтому я решил написать простенький сценарий, который бы разбирал html страницы с драйверами для модели, понимал бы к какой категории относится драйвер или утилита, скачивал бы их, и раскладывал бы в соответствующие папки (при необходимости создавая эти папки самостоятельно).

На вход сценарий принимает адрес страницы с которой необходимо скачать драйверы, например такой http://h20000.www2.hp.com/bizsupport/TechSupport/SoftwareIndex.jsp?lang=ru&cc=ru&prodNameId=462858&prodTypeId=321957&prodSeriesId=462857&swLang=33&taskId=135&swEnvOID=1093

Если вы приглядитесь к кнопкам “Загрузить” на этой странице, то вы увидите что в них содержится ftp ссылка на файл, так что для закачки нам не подойдет простой System.Net.WebClient или BitsTransfer а понадобится System.Net.FtpWebRequest. Он несколько сложнее в использовании, но я нашел хороший пример функции для скачивания файлов по FTP здесь, и включил его в тело сценария. Разумеется можно просто поместить его в свой профиль, но я посчитал что лучше сделать сценарий самодостаточным и независимым от окружения.

Кроме страницы для скачивания, сценарию можно указать еще параметры Destination (папка куда скачивать файлы, по умолчанию текущий каталог) и ключ Plain (если он указан, то файлы сваливаются в одну кучу, без раскладывания по папкам).

Ну и наконец Get-HPDrivers.ps1:

param ($Url, $Destination=$Pwd, [switch]$Plain)            

#Функция Get-FTPFile взята отсюда - http://powershell.com/cs/media/p/804.aspx
function Get-FTPFile ($Source,$Target,$UserName,$Password)
{              

 # Create a FTPWebRequest object to handle the connection to the ftp server  
 $ftprequest = [System.Net.FtpWebRequest]::create($Source)              

 # set the request's network credentials for an authenticated connection  
 $ftprequest.Credentials =
     New-Object System.Net.NetworkCredential($username,$password)              

 $ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
 $ftprequest.UseBinary = $true
 $ftprequest.KeepAlive = $false              

 # send the ftp request to the server  
 $ftpresponse = $ftprequest.GetResponse()              

 # get a download stream from the server response  
 $responsestream = $ftpresponse.GetResponseStream()              

 # create the target file on the local system and the download buffer  
 $targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create)
 [byte[]]$readbuffer = New-Object byte[] 1024              

 # loop through the download stream and send the data to the target file  
 do{
     $readlength = $responsestream.Read($readbuffer,0,1024)
     $targetfile.Write($readbuffer,0,$readlength)
 }
 while ($readlength -ne 0)              

 $targetfile.close()
}              

#Выводим на экран текущее выполняемое действие:
Write-Progress "Receiving metadata" "Downloading"
#Создаем объект WebClient для выкачивания текста страницы
$wc = new-object System.Net.WebClient
#Устанавливаем для него кодировку
$wc.Encoding = [System.Text.Encoding]::GetEncoding("UTF-8")
#Указываем что надо использовать учетные данные той учетной записи 
#под которой запущен сценарий
$wc.UseDefaultCredentials = $true
#Загружаем страницу в переменную $Page
$Page = $wc.DownloadString($url)            

#Обновляем статус
Write-Progress "Receiving metadata" "Parsing HTML"
$Meta = $(
    #С помощью конструкции switch разбираем текст страницы используя регулярные выражения
    #Так как разные элементы (категория, имя, ссылка) находятся на разных строках, я использовал
    #переменные $Category и $Name для сохранения их значений
 switch -regex ($page.split("`n"))
 {
        #Это категория, устанавливаем переменную $Category
  '<a name="\d+"></a><b>([^<]+)</b>' {$Category = $matches[1]}
        #Это имя пакета, помещаем его в $Name
  'SoftwareDescription.jsp[^"]+">([^<]+)' {$Name = $matches[1]}
        #Это уже ссылка
  'targetPage=([^&]+exe)' {
            #Размаскировываем ссылку и помещаем в переменную $Link
   $Link = [system.uri]::UnescapeDataString($matches[1])
            #Создаем новый объект со свойствами Name, Category и Link.
   New-Object PSObject -Property @{Name=$Name; Category=$Category; Link = $Link}
  }
 }
)
#Страница разобрана, полученные объекты помещены в массив $Meta            

#Получаем имя для файла со списком распарсенного, 
#он будет лежать для справки в корне целевой папки
$FileName = Join-Path $Destination "Index.csv"
#Отображаем состояние
Write-Progress "Exporting Metadata" $FileName
#Экспортируем $Meta в файл
$Meta | Export-Csv $FileName -Encoding UTF8 -NoTypeInformation -Delimiter ","            

#Для каждого объекта в массиве $Meta...
foreach ($Driver in $Meta)
{
    #Выводим статус и имя текущего драйвера/программы
 Write-Progress "Downloading files" $Driver.Name
    #Если не указан ключ $Plain то...
 if (!$Plain)
 {
        #Определяем папки для категории и файла, и при необходимости создаем их
  $CategoryFolder = Join-Path $Destination $Driver.Category
  if (-not (test-path $CategoryFolder)) {md $CategoryFolder | out-null}
  $DriverFolder = Join-Path $CategoryFolder $Driver.Name.replace("/","")
  if (-not (test-path $DriverFolder)) {md $DriverFolder | out-null}
        #Устанавливаем имя под которым будет сохранен файл в соответствующей папке
  $FileName = Join-Path $DriverFolder ($Driver.Link -replace '^.+/')
 }
 Else
 {
        #Устанавливаем имя под которым будет сохранен файл в корне папки
  $FileName = Join-Path $Destination ($Driver.Link -replace '^.+/')
 }
    #Если файл еще не существует...
 if (-not (test-path $FileName)) {
        #Вызываем функцию Get-FTPFile для его скачивания
  Get-FTPFile -Source $Driver.Link -Target $Filename -User "Anonymous" -Pass "anon@hp.com"
 } else {
        #Иначе выдаем предупреждение, и продолжаем работу
  Write-Warning "File $FileName already exists, skipping..."
 }
}

Так как моделей у меня было много, я не стал для каждой ссылки вызывать сценарий, а просто сохранил ссылки в текстовый файл urls.txt и запустил следующую команду:

Get-Content .\urls.txt | foreach { C:\Root\Get-HPDrivers.ps1 -Url $_ }

Ну и вот как то так оно выглядит в работе :)

hpdownload

Стоит выразить отдельную благодарность Hewlett-Packard за хорошо структурированную страницу, которую можно разобрать подобным образом. Не многие поставщики способны похвастаться таким.


Курс по PowerShell (6434A) в моём исполнении :)

вторник, января 26, 2010

teacher1 10-12 марта, в Москве, в учебном центре Инвента, я буду читать курс по Windows PowerShell. Это официальный Microsoft’овский курс – 6434A – Automating Windows Server 2008 Administration with Windows PowerShell. С его программой можно ознакомиться тут.

Хотя в названии и фигурирует Windows Server 2008 – большая часть информации касается PowerShell вообще, независимо от операционной системы, а Windows Server 2008 используется лишь для демонстрации функционала PowerShell.

Курс подойдет и для людей совсем не знакомых с PowerShell, и для уже обладающих некоторыми знаниями. Обучение начинается с установки PowerShell и рассмотрения базового функционала. Затем идёт работа с конвейерами, форматированием вывода, и конструкциями контроля выполнения, и т.д. Ближе к концу вы научитесь использовать возможности PowerShell для работы с WMI, ADSI, COM, и .NET на реальных примерах. В курсе много лабораторных работ, что очень важно для хорошего усвоения материала.

Внимание, бонус! Курс не ограничивается программой курса :) Если мы будем укладываться по времени – можно пытать меня всевозможными вопросами, я с удовольствием расскажу вам всё что знаю о PowerShell, лишь бы это было интересно и другим слушателям :)

Продолжительность курса – 3 дня, с 9 утра, до 17 вечера.

Стоимость – 13500 руб.

Записаться можно тут. И лучше сделать это пораньше, так как количество мест ограничено.

До встречи :)


Интересные примеры на PowerShell

воскресенье, января 10, 2010

perl Недавно натолкнулся вот на этот пост. В нём приведено несколько интересных примеров того “как в Perl одна-две строчки кода могут сделать больше, чем десять строк в каком-нибудь другом языке программирования” ;) Мне показалось что будет интересно сравнить как эти примеры выглядели бы на PowerShell ;) И главное – это может быть полезно тем кто уже знает Perl но сейчас изучает PS.

Как известно PowerShell очень молодой язык, и разумеется он унаследовал множество элементов других языков, и следовательно местами схож с многими из них. Я часто слышу о коде PowerShell фразы типа “О, да они же украли PHP!”, “Это же C# с более простым синтаксисом”. Но по-моему больше всего PowerShell похож на Perl. Это и не удивительно – Perl был одним из любимых языков авторов PS, и это здорово – многим хорошим особенностям в PS мы обязанны именно Perl’у.

Hats off to superstar Larry Wall and Perl, very few people and technologies that have had the level of (positive :-) ) impact these 2 have had on the industry.  The world is a better place because that guy was born!

Jeffrey Snover


Это была отмазка ;) Теперь перейдем к коду :)

Сразу скажу – мне кажется это просто удачная подборка попалась, многие примеры удалось записать на PS сильно короче, и главное – понятнее (субъективно конечно :) ). В очень многих областях Perl легко даст фору PowerShell’у в плане компактности кода, может быть даже и некоторые из этих примеров на Perl можно записать гораздо более красиво, так что буду рад если знающие Perl люди оставят свои конструктивные комментарии :)

Я цитирую только описания, код на Perl можно посмотреть в оригинальном посте.

1. Проверить, существует ли элемент (первый аргумент функции, передается по значению) в массиве (второй аргумент функции, передается по ссылке).

#Встроенный оператор -contains
$array -contains $element            

2. Удалить из массива @arr элементы, которые есть в массиве @skip.

#Вопросительный знак - алиас для where
$arr | ? {$skip -notcontains $_}

Вместо пункта 3 я написал красивый (субъективно разумеется ;) ) фильтр:

filter Replace-Words
{
    foreach ($arg in $args) {
        $pair = $arg.split("=",2) #Разрезаем аргумент на 2 части по знаку 
        $_ = $_ -replace $pair[0],$pair[1] #Заменяем вхождения в строке
    }
    $_ #Выдаём результирующую строку
}

Использовать например так:

$text = Get-Content .\test1.txt
$text | Replace-Words плохое=хорошее яблоки=груши | Set-Content .\test1.txt

4. Вывести список имен файлов и каталогов в заданной директории, отсортированный по дате последнего доступа. Обычно глобы сортируют список по имени файлов и каталогов. Для сортировки по дате последнего изменения, заменить цифру 8 на 9.

В PS для сортировки по дате изменения надо заменить не 8 на 9, а LastAccessTime на LastWriteTime ;)

ls | sort lastAccessTime

5. Удалить повторяющиеся элементы в массиве.

$arr | select -Unique

6. Перемешать элементы массива

$arr | sort {Get-Random}

Командлет Get-Random появился только в PS 2.0, в 1.0 можно сделать так:

$r = New-Object random
$arr | sort {$r.next()}

7. Выбрать случайный элемент в массиве можно как минимум двумя способами. Можно перемешать элементы, как в предыдущем примере, и выбрать нулевой, а можно в одну строчку:

$arr | Get-Random

8. Аналог PHP функции urlencode.

[System.Uri]::EscapeUriString(http://проверка)

Создание локального пользователя и мешающее ничто

пятница, ноября 6, 2009

Недавно ко мне обратились с вопросом о странном поведении достаточно простой функции. Она создаёт локального пользователя с заданным именем и паролем, и должна возвращать в качестве результата объект с двумя свойствами: UserName и Password (Ну на самом деле функция была чуть сложнее, но нам интересна только эта часть :) ). Вот её код:

function  CreateUser ($Name, $Password)
{
    trap {"Error creating user!"; break} #Перехват ошибок
 $computer = [ADSI]"WinNT://$env:computername" #Подключаемся к локальному компьютеру по ADSI
 $user = $computer.Create("user", $Name) #Создаём объект типа user
 $user.SetPassword($Password) #Устанавливаем пароль
 $user.SetInfo() #Записываем изменения            

 $UP = New-Object PSObject #Создаём пустой объект
 $UP | Add-Member NoteProperty Username $Name #Добавляем свойства
 $UP | Add-Member NoteProperty Password $Password #И ещё
 $UP #Возвращаем объект
}

Теперь попробуем создать пользователя с помощью этой функции:
PS C:\Windows\system32> $Result = createUser -name Tester -pass P@ssw0rd
PS C:\Windows\system32> $Result
Username Password
-------- --------
Tester P@ssw0rd

PS C:\Windows\system32> $Result.UserName
PS C:\Windows\system32> $Result.Password
PS C:\Windows\system32>

Странно? Если просто вызываем переменную, то видно 2 свойства: UserName, Password, и их значения. Однако при попытке просто получить значения этих свойств мы получаем… ничего. В чём же дело?
PS C:\Windows\system32> $Result.gettype().name
Object[]
PS C:\Windows\system32> $Result.count
3

Квадратные скобки в конце типа объекта, говорят о том что это не просто объект, а массив. Причем как говорит его свойство .Count – из трех элементов. Зная это можно обратиться к свойствам:
PS C:\Windows\system32> $Result[2].Username
Tester
PS C:\Windows\system32> $Result[2].Password
P@ssw0rd

Нужный нам элемент будет лишь третьим по счёту (массивы индексируются с 0). Что касается первых двух элементов:
PS C:\Windows\system32> $Result[0].gettype().name
You cannot call a method on a null-valued expression.
At line:1 char:19
+ $Result[0].gettype <<< $Result[0] -eq $null
True

Их просто не существует, точнее они равны $null, пустоте. А взялись они благодаря вызовам методов ADSI SetPassword() и SetInfo(). Уж не знаю зачем, но видимо авторы данных методов посчитали это прикольным :) А в PowerShell есть одна тонкость: все данные которые не присваиваются переменной, не передаются по конвейеру, и не обрабатываются другим способом – отправляются на вывод. То есть функция function test {Get-process powershell; "Test"; del c:\test.txt; return 2+1; 2} вернёт вам массив состоящий из объекта процесса, строки Test и числа 3. Двойка в конце не вернётся лишь потому что return предотвращает дальнейший вывод данных (возвращает не единственный результат, а лишь последний из ряда).
Короче говоря в нашей ситуации $null’ы возвращённые методами, присоеденились к результату возвращаемому функцией, и всё испортили :) Чтобы этого не произошло, можно избавиться от этих $null’ов, например… отправив их в Null :)
$user.SetPassword($Password) | Out-Null
$user.SetInfo() | Out-Null

Выдираем ссылки из вебстранички

суббота, октября 31, 2009

Для большинства случаев использования регулярных выражений в PowerShell применяются операторы -match, -replace, и командлет Select-String. Но иногда их возможностей недостаточно, и тогда на помощь приходит класс [regex] принося всю мощь регулярных выражений .Net :) Одно из самых простых его применений – выборка из куска текста нескольких вхождений одного выражения. Для примера – достанем ссылки из кода вебстраницы. Сразу замечу что выражение определяющее url не точное, но в большинстве случаев его будет более чем достаточно.
Итак, для начала объявим функцию для загрузки кода веб-страницы (это обновлённая версия Get-WWWString):

function Get-WwwString ([string]$Url, [string]$Encoding="windows-1251", [System.Management.Automation.PSCredential]$ProxyCredential = $GlobalCreds)
{
        $wc = new-object System.Net.WebClient
        $wc.Encoding = [System.Text.Encoding]::GetEncoding($Encoding)
        $wc.UseDefaultCredentials = $true
        if ($ProxyCredential) {$wc.Proxy.Credentials = $ProxyCredential.GetNetworkCredential()}
        $wc.DownloadString($url)
}

Теперь загружаем страницу, и вытаскиваем ссылки…

$Text = Get-WwwString "http://ya.ru"            

[regex]$reg = '"(\w+://[^"]+)"'
$match = $reg.match($Text)
while ($match.Success)
{
    $match.Groups[1].value
    $match = $match.nextMatch()
}

Windows Management Framework released!

вторник, октября 27, 2009

Около часа назад наконец произошло то чего все долго ждали :) Вышел в релиз Windows Management Framework! Это комплект в который входят следующие компоненты:

  • WinRM 2.0
  • Windows PowerShell 2.0
  • BITS 4.0

Он выпущен для следующих операционных систем:

  • Windows XP
  • Windows Server 2003
  • Windows Vista
  • Windows Server 2008

Да, да, релиз PowerShell v2 наконец то наступил для всех (ранее он был доступен лишь в составе Windows 7 и Windows Server 2008 R2)

Скачать можно тут – http://support.microsoft.com/kb/968929.

PowerShell доступен через WSUS!!!

среда, марта 25, 2009

Чудо свершилось! Windows PowerShell наконец то доступен как обновление на WSUS!

wsus-ps

 

Это решает сразу две проблемы: во-первых разворачивать PowerShell в организации стало проще простого. Ведь раньше дистрибутивы PowerShell были доступен лишь в виде исполняемого файла .exe, который нельзя было развернуть с помощью групповых политик. Требовалось использовать startup-сценарии или платные системы вроде SMS, SCCM, SCE или аналогов. А сейчас достаточно лишь одобрить обновление )

Во вторых, PowerShell будет гораздо быстрее распостранятся в организациях. Если раньше его установка была “установкой дополнительного ПО”, то теперь это системное обновление. А уж учитывая то что PowerShell начиная с Windows 2008 встроен в систему, а с Windows 7 и Windows 2008R2 вообще предустановлен, и даже доступен в Server Core…

Если вы профессионально занимаетесь Windows технологиями и автоматизацией, но еще почему то не занялись изучением PowerShell – очень рекомендую заняться устранением этого пробела ;)

PS: Если у вас PowerShell не виден в списке обновлений, скорее всего это из за того что не включена загрузка “Пакетов новых функций”, или “Feature Updates”.

Перевод PowerShell блогов

четверг, марта 19, 2009

Благодаря организаторской деятельности Андрея Бешкова, и труду Виктора Горбункова и Ильи Лушникова теперь есть возможность читать переведеднные на русский язык блоги разработчиков PowerShell и Active Directory PowerShell. Русские версии доступны соответственно по следующим адресам - http://blogs.technet.com/powershell_ru/ и http://blogs.technet.com/adpowershell_ru/.

Конечно если знание английского языка позволяет – лучше читать оригинальные блоги, ведь идеальный перевод к сожалению невозможен, особенно если речь идёт о таких сложных технологиях. Но к сожалению это не всегда возможно, и тут на выручку придут эти русскоязычные версии.

В нашем полку прибыло

понедельник, марта 16, 2009

Приятно видеть что русскоязычных авторов по теме PowerShell становится всё больше, я еще помню время когда мой блог был чуть ли не единственным русскоязычным ресурсом )

Но время идёт, и информации на русском языке прибавляется. Сегодня я хотел бы порекомендовать статью Сергея Вальковского, которую вы можете прочитать тут. Мне очень понравился как стиль изложения, так и то что статья отличается от стандартных “введений в PowerShell”, и знакомит с языком на примере реальных задач (заезженная фраза, но ничего ) ).

Ссылка на статью помещена в мой склад ресурсов по PowerShell – http://windowspowershell.ru

Если у вас есть предложения о дополнении коллекции – шлите по электронке, или оставляйте в комментариях.

PoshCode.org – Работа из PowerShell

среда, марта 11, 2009

В прошлом своём посте я рассказал о нескольких способах поиска на сайте PoshCode.org, но забыл упомянуть о самом главном – из самого PowerShell )

Исправляю оплошность. На сайте доступны два набора команд для работы с сайтом, одна в виде обычного файла .ps1 для PowerShell v1.0, и другая в виде модуля для PowerShell v2.0 CTP3.

В первом случае вам надо загрузить файл v1-compatible script и импортировать функции из него в текущую сессию PowerShell выполнив команду

. c:\scripts\PoshCode.ps1

Обратите внимание на точку и пробел за ней, эта конструкция необходима чтобы PowerShell не просто выполнил код из файла, но сохранил функции в текущей сессии. Чтобы иметь доступ к ним постоянно, можно поместить код в файл $profile.

Для PowerShell V2 загружайте v2 PoshCode Module for CTP3. Его можно поместить в папку $PSHOME\modules\PoshCode, и затем загружать командой

Import-Module PoshCode

Ну и дальше всё как обычно в PowerShell )

poshcode