sobota 8. srpna 2009

Scripting Games 2009 - Event 3

Třetí úkol se jmenoval The shot put.

Divize Advanced
V zadaném vstupním souboru bylo potřeba najít slova (řádky), které obsahují pouze jednu samohlásku (mohla se opakovat), čili ananas je správně, ale ahoj nikoli.
  1. gc './Wordlist_ADV3.txt' |? {(($_.ToCharArray() -match '[aeiouy]'| group)|measure-object).count -eq 1} | out-file result.txt
Použil jsem one-liner, který provádí následující:
  1. načte vstupní soubor
  2. vstupní řádku převede na pole znaků ($_.ToCharArray()). Pokud je v poli znaků pouze jedna samohláska
  3. vypíše vstupní řádku do souboru
Ani v tomto případě se nejednalo o žádnou záludnost. Je dobré vědět, že řetězec znaků lze převést na pole znaků pomocí metody ToCharArray().

Divize Beginners
Vstupní soubor se měl rozdělit na dva a v každém novém souboru měl být jeden odstavec originálního souboru. Ten se měl poté přejmenovat.
  1. (gc s*)[0..4] | Out-File 'First par.txt'
  2. (gc s*)[6..8] | Out-File 'Second par.txt'
  3. ren 'shot put.txt' 'Shot put.old'
V tomto případě jsem nehodlal ztrácet čas a problém jsem vyřešil jako kdyby to bylo ve skutečnosti - bez nějakého ošetřování pomocí tupého kódu.

Získávání informací z WMI

V jednom z předchozích článků jsem slíbil, že se někdy podrobněji podíváme na přístup do WMI pomocí PowerShellu. Ta doba právě nastala :)
O WMI (Windows Management Instrumentation) jste jistě alespoň slyšeli. Pokud ne, určitě vám doporučuji se podívat, k čemu je WMI dobré - můžete začít na Wikipedii nebo na stránkách Microsoftu. Pro potřeby tohoto článku budu předpokládat, že máte alespoň základní představu o tom, k čemu WMI slouží.
Úkol pro všechny následující příklady bude stejný: zjistit všechny běžící procesy s názvem notepad.exe a vypsat jejich ProcessID. Na konci každého příkladu uvedu průměrný čas rychlosti zpracování pro 10 stejných dotazů.
Filtrování pomocí Where-Object
Zrějmě prvním nápadem bude použití standardních příkazů PowerShellu bez nějakého hlubšího zkoumání dalších okolností.
PS C:\> gwmi Win32_Process |? {$_.Name -eq 'notepad.exe'} | select ProcessId
ProcessId
---------
     2072
     5228
Tento postup je přímočarý a pokud potřebujete z WMI "tahat" data pouze příležitostně, bude vám zřejmě stačit. Pokud budete s WMI pracovat častěji (a budete zpracovávat větší množství dat) začne vám za chvíli vadit rychlost (resp. pomalost) zpracování dat. Nevýhodou tohoto přístupu je totiž to, že čtete z WMI všechny procesy a ty pak rourou posíláte cmdletu Where-Object (v mém příkladu zastoupeném jeho aliasem '?' - znak otazník).
Rychlost: 1535 ms
Parametr -Filter
Cmdlet Get-WmiObject má parametr -Filter. Použítím tohoto parametru filtrujete data již na úrovni WMI a dále pracujete pouze s procesy (objekty), které splňují zadané kritérium.
PS C:\> gwmi Win32_Process -filter "name='notepad.exe'" | select ProcessID

ProcessID
---------
     2072
     5228
Rychlost zpracování v tomto případě je 595 ms. Cmdlet Select-Object (resp. jeho alias select) je v tomto případě použit z toho důvodu, že filter nám vrací všechny vlastnosti a my jsme na začátku řekli, že budeme vypisovat pouze ID procesu.
Parametr -Query
Pokud ovládáte jazyk WQL (WMI Query Language) můžete pomocí něj specifikovat dotaz v parametru -Query. Celý příkaz by pak vypadal následovně:
PS C:\> gwmi -Query "SELECT ProcessID FROM Win32_Process WHERE Name='notepad.exe'"

__GENUS : 2
__CLASS : Win32_Process
__SUPERCLASS :
__DYNASTY :
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ProcessId : 2072

__GENUS : 2
__CLASS : Win32_Process
__SUPERCLASS :
__DYNASTY :
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ProcessId : 5228
PowerShell přidal do výpisu ještě několik vlastností, které nás momentálně nezajímají (začínají dvěma podtržítky). Pokud bychom je chtěli odstranit, můžeme opět použít cmdlet Select-Object. Rychlost zpracování byla 573 ms.
Shrnutí
Z předchozích příkladů je vidět, že pokud chcete pracovat s WMI je dobré si rozmyslet, jakým způsobem k němu budete přistupovat. Pokud občas potřebujete okamžitý výsledek z lokálního počítače (a vámi dotazovaná třída nemá tisíce členů), první postup vám možná bude vyhovovat. V případě většího množství dat (případně dotazů na vzdálený počítač) je dobré se naučit používat parametry -Filter nebo -Query.

čtvrtek 6. srpna 2009

Hej, mistře Šelíku!

V posledním vydání TechNet Flash zpravodaje zaujaly mistra Šelíka dvě věci.
  1. Strip jeho kamaráda mistra Skriptíka.
  2. První díl ze série článků o PowerShellu - snad se nám blýská na lepší časy.
Mistr Skriptík tentokrát řešil práci se službami, pojďme se podívat.

Vzhledem k tomu, že služby jsou jednou ze základních komponent, se kterou se administrátor Windows může potkat, má v sobě PowerShell zabudováno hned několik cmdletů pro práci s nimi.

PS C:\> Get-Help *-service | Select name
Name
----
Get-Service
Stop-Service
Start-Service
Suspend-Service
Resume-Service
Restart-Service
Set-Service
New-Service

My se podíváme na Get-Service, Stop-Service a Start-Service. Pro zjištění stavu všech služeb použijeme Get-Service - dostaneme výpis všech služeb včetně jejich aktuálního stavu (výpis jsem notně zkrátil).
PS C:\> Get-Service
Status Name
------ ----
Stopped ADAM_Test
Stopped Alerter
Running ALG
Stopped AppMgmt
Stopped aspnet_state
Running AudioSrv
Running BITS
Running Browser
Running CryptSvc
Running DcomLaunch
Running Dhcp
Running Eventlog
Running EventSystem
Running Spooler

Pro zastavení služby použijeme Stop-Service (spsv) a pro spuštění Start-Service (sasv). V závorkách jsou uvedeny aliasy pro dané cmdlety (pro Get-Service je to gsv).
PS C:\> gsv spooler

Status Name DisplayName
------ ---- -----------
Running Spooler Print Spooler
PS C:\> spsv spooler
PS C:\> gsv spooler

Status Name DisplayName
------ ---- -----------
Stopped Spooler Print Spooler
PS C:\> sasv spooler
PS C:\> gsv spooler

Status Name DisplayName
------ ---- -----------
Running Spooler Print Spooler

Je vidět, že pomocí PowerShellu je práce se službami přímočará a velice jednoduchá.
Pokud je někdo zvídavý a přecijen by chtěl pracovat se službami přes WMI, lze pro zastavení spooleru použít následující příkaz:
PS C:\> (gwmi Win32_Service -filter "name='spooler'").StopService()

__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 0

Vidíme, že ReturnValue je nula, čili služba byla zastavena úspěšně. Pokud by ReturnValue byla jiná, lze její význam najít na MSDN.
To by bylo pro dnešek vše. Jdu stejně jako mistr Skriptík vyhlížet Windows 7.
Banner mistra Skriptíka je stažen ze stránek TechNet Flash zpravodaje. Děkuji týmu TechNetu za možnost jeho použití pro potřeby tohoto článku.

sobota 1. srpna 2009

Hej, mistře Šelíku!

Po odmlce způsobené pracovní zátěží a poté zaslouženou dovolenou jsme se s mistrem Šelíkem vrátili zpět do práce.

Pro základní práci s procesy lze v PowerShellu použít cmdlet Get-Process (nebo můžete použít alias gps). Při prostém zadání bez parametrů vypíše seznam běžících procesů. Pokud vás zajímá konkrétní proces, stačí uvést jeho jméno.

PS C:\> Get-Process powershell
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
656 14 55592 63296 270 4,29 264 powershell

V případě, že chcete vypsat všechny informace, použijte Format-List
PS C:\> Get-Process powershell | Format-List *
V tomto případě bych se ovšem přikláněl k použítí aliasů

PS C:\> gps powershell | fl *
PS C:\> gps pow* | fl *

přičemž druhý zápis by mohl způsobit "problémy" pokud na vašem počítači poběží proces s názvem např. powercfg.

Jistě jste si všimli, že ve výpisu není nikde vidět vlastník procesu. Je tedy nutné - na radu mistra Skriptíka - použít přístup přes WMI. V PowerShellu je přístup do WMI geniálně jednoduchou záležitostí. Je několik možností, jak cmdlet Get-WmiObject použít. Nyní je nebudu porovnávat, ale beru to jako námět na další článek. Nebudu ani vypisovat výsledek, protože to vždy bude jméno uživatele, který proces spustil. Při práci z příkazové řádky vždy používám pro Get-WmiObject jeho alias gwmi - ušetřím tím přesně $("get-wmiobject".length - "gwmi".length) znaků :)

PS C:\> gwmi win32_process |? {$_.name -eq 'powershell.exe'} | % {$_.GetOwner().User}
PS C:\> (gwmi win32_process |? {$_.name -eq 'powershell.exe'}).GetOwner().User
PS C:\> (gwmi win32_process -filter "name='powershell.exe'").GetOwner().user
PS C:\> (gwmi -query "select * from Win32_Process where name='powershell.exe'").GetOwner().User

Banner mistra Skriptíka je stažen ze stránek TechNet Flash zpravodaje. Děkuji týmu TechNetu za možnost jeho použití pro potřeby tohoto článku.

čtvrtek 25. června 2009

Scripting Games 2009 - Event 2

Úkol číslo dva byl nazván The Long Jump.

Divize Advanced
Z daného souboru (XLS) bylo potřeba zjistit délky skoků jednotlivých sportovců a na základě výkonů v sezóně určit zda splnili očekávání do nich vkládaná.

  1. $path = "C:\sg2009\LongJump_Adv2.xls"
  2. $xls = New-Object -com Excel.Application
  3. $xls.Visible = $true
  4. $wrkbk = $xls.Workbooks.Open($path)
  5. $format = 23
  6. $wrkbk.SaveAs('C:\sg2009\result.csv',$format)
  7. $wrkbk.Close($true)
  8. $xls.Quit()
  9. $data = Import-Csv C:\sg2009\result.csv
  10. foreach ($row in $data) {
  11. if ($row.'Jump 1' -eq 'x') { $row.'Jump 1' = 0 }
  12. if ($row.'Jump 2' -eq 'x') { $row.'Jump 2' = 0 }
  13. if ($row.'Jump 3' -eq 'x') { $row.'Jump 3' = 0 }
  14. if (([double]$row.'Jump 1' -ge [double]$row.'Jump 2') -and ([double]$row.'Jump 1' -ge [double]$row.'Jump 3')) {
  15. [double]$row.Result = $row.'Jump 1'
  16. }
  17. elseif (([double]$row.'Jump 2' -ge [double]$row.'Jump 1') -and ([double]$row.'Jump 2' -ge [double]$row.'Jump 3')) {
  18. [double]$row.Result = $row.'Jump 2'
  19. }
  20. else {
  21. [double]$row.Result = $row.'Jump 3'
  22. }
  23. if ($row.Result -lt $row.'Season Average') {
  24. $row.'Exceed/Achieve/Under Perform ' = '1.Under Perform'
  25. }
  26. elseif ($row.Result -eq $row.'Season Average') {
  27. $row.'Exceed/Achieve/Under Perform ' = '2.Achieve'
  28. }
  29. else {
  30. $row.'Exceed/Achieve/Under Perform ' = '3.Exceed'
  31. }
  32. }
  33. $data | sort 'Exceed/Achieve/Under Perform ', Result -Descending | `
  34. Export-Csv C:\sg2009\result.csv -Force -NoTypeInformation
Celou úlohu jsem si převedl do PowerShellu tím, že jsem zavolal Excel a použil SaveAs... / CSV. Potom jsem již jen načel data pomocí Import-Csv a porovnal výsledky za sezónu. Výsledek jsem zapsal do potřebného sloupce a poté jen výsledky vyexportoval do nového CSV souboru.

Divize Beginners
Obsahovala jednoduchou úlohu pro dotaz do WMI. V PowerShellu naprosto jednoduchá věc. Celá úloha byla pouze "ztížena" výpisem různých barviček. Tentokrát jsem úkol řešil jako jednoduchou funkci, kdy jsem navíc přidal možnost zadání jména vzdáleného počítače.

  1. function Get-LongJumpResults {
  2. param (
  3. $computer = "localhost"
  4. )
  5. $headerColor = "Green"
  6. $speedColor = "Yellow"
  7. $strengthColor = "Magenta"
  8. $agilityColor = "Cyan"
  9. $data = gwmi Win32_Processor -ComputerName $computer
  10. Write-Host "Strength evaluation for $computer" -ForegroundColor $headerColor
  11. Write-Host "Speed ... $($data.MaxClockSpeed)" -ForegroundColor $speedColor
  12. Write-Host "L2 cache size: $($data.L2CacheSize)" -ForegroundColor $speedColor
  13. Write-Host "L2 cache speed: $($data.L2CacheSpeed)" -ForegroundColor $speedColor
  14. Write-Host "L3 cache size: $($data.L3CacheSize)" -ForegroundColor $speedColor
  15. Write-Host "L3 cache speed: $($data.L3CacheSpeed)" -ForegroundColor $speedColor
  16. Write-Host "Strength ..." -ForegroundColor $strengthColor
  17. Write-Host "Number of Cores: $($data.NumberOfCores)" -ForegroundColor $strengthColor
  18. Write-Host "Number of logical processors: $($data.NumberOfLogicalProcessors)" -ForegroundColor $strengthColor
  19. Write-Host "Name: $(($data.Name).trim())" -ForegroundColor $strengthColor
  20. Write-Host "Agility ..." -ForegroundColor $agilityColor
  21. Write-Host "Address width: $($data.AddressWidth)" -ForegroundColor $agilityColor
  22. } #function Get-LongJumpResults
Opět nic složitého.

pátek 19. června 2009

Audit skupin v Active Directory

Dnes mě na chatu chytil jeden z přátel zabývajících se bezpečností. Líbil se mu report zobrazující lokální administrátory na koncových stanicích a chtěl něco podobného vytvořit pro skupinu Domain Admins. Ptal se mne na možnosti a mou první odpovědí bylo: PowerShell.

První nástřel byl tento:
Get-QADGroupMember 'domain.com/CZ/Domain Admins'

Výstupem příkazu je seznam členů této skupiny. To mělo úspěch a samozřejmě přišla další otázka: "Lze porovnat výsledky proti nějakému danému stavu?" Ano, úkol jak dělaný pro Compare-Object. Celý skript pak vypadal následovně.
  1. function Compare-ADGroup {
  2. param (
  3. [string]$group = 'domain.com/CZ/Domain Admins',
  4. [switch]$snapshot
  5. )
  6. if ($snapshot) {
  7. # save state as snapshot
  8. Get-QADGroupMember $group | `
  9. Select-Object SamAccountName, FirstName, LastName | `
  10. Export-Clixml ./snapshot.xml
  11. }
  12. else {
  13. Get-QADGroupMember $group | `
  14. Select-Object SamAccountName, FirstName, LastName | `
  15. Export-Clixml ./current.xml
  16. # load to variables, sort to prepare for Compare-Object
  17. $s = (Import-Clixml ./snapshot.xml) | Sort-Object SamAccountName
  18. $c = (Import-Clixml ./current.xml) | Sort-Object SamAccountName
  19. # compare objects based on SamAccountName
  20. Compare-Object -ReferenceObject $s -DifferenceObject $c -Property SamAccountName
  21. } # else
  22. } # function Compare-ADGroup
První spuštění musí být s parametrem -Snapshot. Tím se vytvoří referenční soubor se "správnými" daty. Skript by šel ještě vylepšit o kontrolu existence souboru snapshot.xml nebo například o vložení časové značky do souboru current.xml.

středa 10. června 2009

Scripting Games 2009 - Event 1

Určitě vám neuniklo, že byl neoficiálně odstartován další ročník Scripting Games. Nejlepším rozcestníkem je nyní zřejmě stránka PoshCode.org, která zajišťuje prostor pro vkládání hotových skriptů. První úkol má název The 100 meter dash.

Divize Advanced
Úkolem je najít ve vstupním textovém souboru řádky obsahující text a vypsat tři nejkratší. Mé řešení bylo následujcí:

Get-Content "./Personal Information Cards_ADV1.txt" | `
Where-Object {$_ -match "\S"} | `
Sort-Object Length | `
Select-Object -First 3

Celá práce probíhá na jedné řádce.
  1. Načtení souboru.
  2. Vyfiltrování řádek, které obsahují text. Zde vyjádřeno pomocí regulárního výrazu \S - pusť všechno, co není pouze mezera.
  3. Seřaď řádky podle délky.
  4. Vypiš tři nejkratší.
Celý úkol byl relativně jednoduchý, důležité bylo přijít na filtrování prázdných řádek. Někteří použili v druhém kroku funkci trim().

Divize Beginners
Byla na rozdíl od advanced relativně složitá. Vše bylo způsobeno jedním malým "překlepem" kdy ve vstupním souboru byla jedna položka oddělena mezerou místo tabulátorem. Jelikož jsem chtěl mít hotovo rychle, využil jsem letošního pravidla: "Vše, co je dovoleno v reálném světě, je dovoleno i v soutěži." Ručně jsem opravil "chybnou" řádku a skript byl poté již jednoduchý:

Import-Csv '100 Meter Event.txt' -Delimiter "`t" | `
  Sort-Object Time | `
  Select-Object -First 3
  1. Použil jsem cmdlet Import-Csv z PowerShellu verze 2. Ve verzi 2 je totiž u tohoto cmdletu nový parametr, pomocí nějž určíte, jak je soubor oddělen. Výborná věc pro české prostředí, kde je standardním oddělovačem středník.
  2. Výstupem z první roury byly objekty, které jsem seřadil podle času
  3. a pak jen vypsal první tři.
K tomuto úkolu se ještě vrátím a pomocí regulárních výrazů si poradím s problémovou řádkou.

Jelikož jsem u obou řešení používal aliasy, můžete se na mé "opravdové" verze v beginners a advanced divizích podívat přímo na PoshCode.org.