Scripting Games 2009 Бег на 110 метров с барьерами.

Beginner Division

Задача:

  • В исходном файле скрипта найти все ошибки и исправить их. При запуске скрипта на рабочем столе должны появиться 3 ярылка: на сам скрипт, на веб-сайт Hey, Scripting Guys! и ярлык на блокнот (notepad.exe).

Решение:

Всё копировать не буду, а только приведу уже готовый скрипт. Сам текст скрипта можно взять тут: Hey, Scripting Guy! Opening Ceremonies and 2009 Scripting Games Event 6 Details (Beginner and Advanced; 110-meter hurdles). Исходные ошибочные строки привёл в комментарии:

#==========================================================================
#
# PowerShell:  AUTHOR: Ed Wilson , msft,  6/15/2009
#
# NAME: Beg_6.ps1
#
# COMMENT: Key concepts are listed below:
#1. Uses wscript.shell to create three shortcuts on the desktop. The first is a shortcut
#2. to this actual script. It uses the scriptfullName property to assign the path.
#3. The second is a simple Web site URL shortcut. The third one is a shortcut to
#4. Notepad.
#==========================================================================
$ErrorActionPreference = "SilentlyContinue"
Set-PSDebug -Strict
New-Variable -Name objShell #instance of the wshSHell object
New-Variable -Name strDesktop #pointer to desktop special folder
New-Variable -Name objShortCut #used to set properties of the shortcut. Comes from using createShortCut
New-Variable -Name objURL #used to set properties of webshortcut.
# было: $oShell = New-Object -ComObject ("WScript.Shell") - ошибка в имени переменной
$objShell = New-Object -ComObject ("WScript.Shell")
$strDesktop = $objShell.SpecialFolders.item("Desktop")

$objShortCut = $objShell.CreateShortcut($strDesktop + "\Shortcut Script.lnk")
# было: $objShortCut.TargetPath = $MyInvocation.ScriptName - неверное свойство
$objShortCut.TargetPath = $MyInvocation.MyCommand
$objShortCut.WindowStyle = 0
$objShortCut.Hotkey = "CTRL+SHIFT+F"
$objShortCut.IconLocation = "notepad.exe, 2"
$objShortCut.Description = "Shortcut Script"
$objShortCut.WorkingDirectory = $strDesktop
# было: $objShortCut.Save - при вызове метода забыли скобки
$objShortCut.Save()

# $wshShell = New-Object -ComObject wscript.shell - не нужно совсем
# было: $objURL = $objShell.CreateShortcut($strDesktop & "\The Microsoft Scripting Guys.url")
# вместо + поставили & из VBS
$objURL = $objShell.CreateShortcut($strDesktop + "\The Microsoft Scripting Guys.url")
$objURL.TargetPath = "http://www.ScriptingGuys.com"
# было: $objURL.Discription = "Scripting Guys". Такого свойства в URL файлах нету.
$objURL.Save()

# было: $wshNetwork = New-Object -ComObject wscript.network - не нужно свсем
# было: $objShortCut = $objShell.CreateShortcut($strDesktop + "\notepad.link")
# расширение не LNK, а LINK
$objShortCut = $objShell.CreateShortcut($strDesktop + "\notepad.lnk")
# было: $objShortCut.TargetPath = "notpad.exe" - пропустили букву E
$objShortCut.TargetPath = "notepad.exe"
$objShortCut.IconLocation = "notepad.exe, 0"
$objShortCut.description = "notepad"
$objShortCut.Save()

Если запустить этот скрипт из файла, то получится 3 ярлыка и первый будет вести именно на файл самого скрипта.

Advanced Division

и снова задача на парсинг текста. Потребуется файл network trace_adv6.txt из Competitors pack.

Задача:

  • распарсить вывод tracert и показать участки, на которых наблюдается замедление скорости (т.е. время прыжка увеличивается)

Решение:

Самая первая задача, которая перед нами встаёт – создание регулярного выражения. Выражение у меня получилось вот такое:

^\d+\W+(\d+)\D+(\d+)\D+(\d+) ms  (.*)

Что оно делает: оно откидывает порядковый номер прыжка (hop), любую небуквенную последовательность (в общем смысле [A-Za-z0-9]) и выбирает первую последовательность чисел. Эта последовательность будет будет являться временем первой попытки посылки ICMP пакета. Далее откидывается любая нечисловая последовательность и выбирается первая же числовая последовательность, которая будет являться второй попыткой посылки ICMP пакета. То же самое делаем до 3-й попытки текущего прыжка включительно. После выкидываем пробел и последовательность букв “ms”, за ней 2 пробела и захватываем всё до конца строки, что будет являеться адресом и/или именем маршрутизатора. Т.к. перед порядковыми номерами прыжков (до 9 включительно) присуствует пробел, то его можно средать методом Trim() и уже подставлять выражение. Из захваченного времени нужно на лету посчитать среднее значение. И вот что у меня вышло:

[↓] [vPodans] gc "Network Trace_Adv6.txt" | ?{$_.trim() -match "^\d+\W+(\d+)\D+(\d+)\D+(\d+) ms (.*)"} | %{ >> $current = "" | Select @{n='Average';e={[int](([int]$matches[1]+[int]$matches[2]+[int]$matches[3])/3)}}, >> @{n='Name';e={$matches[4]}} >> $current >> } | ft -a >> Average Name ------- ---- 1 r10.ntwk.nwtraders.com [192.168.227.82] 1 r20.ntwk.nwtraders.com [192.168.169.1] 1 r50.ntwk.nwtraders.com [192.226.42.47] 13 r70.ntwk.nwtraders.com [192.226.42.12] 34 r12.ntwk.nwtraders.com [192.226.42.9] 34 r40.ntwk.nwtraders.com [192.226.42.22] 104 r22.ntwk.nwtraders.com [192.226.34.80] 105 r32.ntwk.nwtraders.com [192.226.47.10] 125 r35.ntwk.nwtraders.com [192.226.41.198] 173 r37.ntwk.nwtraders.com [192.226.38.14] 173 r38.ntwk.nwtraders.com [192.226.226.119] 389 192.168.216.5 390 r60.ntwk.nwtraders.com [192.168.236.9] [↓] [vPodans]

Теперь нужно определить на каких участках происходит замедление. Я решил поэтапно циклом For проверять каждый элемент массива – меньше ли он, чем следующий элемент или нет. Если он равен или больше следующего элемента, то ничего делать не надо, т.к. замедления нету. Если меньше, то он является пограничным этапом, где происходит замедление и выводим его на экран. Привожу уже готовый код и полученный результат:

$sum = @()
gc "Network Trace_Adv6.txt" | ?{$_.trim() -match "^\d+\W+(\d+)\D+(\d+)\D+(\d+) ms  (.*)"} | %{
    $current = "" | Select @{n='Average';e={[int](([int]$matches[1]+[int]$matches[2]+[int]$matches[3])/3)}},
    @{n='Name';e={$matches[4]}}
    $sum += $current
}
$(for ($i = 0; $i -le $sum.Count; $i++ ) {
    if ($sum[$i].Average -lt $sum[$i + 1].Average) {$sum[$i]}
    if ($i -eq ($sum.Count - 1)) {$sum[$i]}
}) | ft @{l='Average';e={$_.Average};a='left'}, @{l='Name';e={$_.name}} -AutoSize

[↓] [vPodans] $sum = @() [↓] [vPodans] gc "Network Trace_Adv6.txt" | ?{$_.trim() -match "^\d+\W+(\d+)\D+(\d+)\D+(\d+) ms (.*)"} | %{ >> $current = "" | Select @{n='Average';e={[int](([int]$matches[1]+[int]$matches[2]+[int]$matches[3])/3)}}, >> @{n='Name';e={$matches[4]}} >> $sum += $current >> } >> $(for ($i = 0; $i -le $sum.Count; $i++ ) { >> if ($sum[$i].Average -lt $sum[$i + 1].Average) {$sum[$i]} >> if ($i -eq ($sum.Count - 1)) {$sum[$i]} >> }) | ft @{l='Average';e={$_.Average};a='left'}, @{l='Name';e={$_.name}} -AutoSize >> Average Name ------- ---- 1 r50.ntwk.nwtraders.com [192.226.42.47] 13 r70.ntwk.nwtraders.com [192.226.42.12] 34 r40.ntwk.nwtraders.com [192.226.42.22] 104 r22.ntwk.nwtraders.com [192.226.34.80] 105 r32.ntwk.nwtraders.com [192.226.47.10] 125 r35.ntwk.nwtraders.com [192.226.41.198] 173 r38.ntwk.nwtraders.com [192.226.226.119] 389 192.168.216.5 390 r60.ntwk.nwtraders.com [192.168.236.9] [↓] [vPodans]

А теперь обратите внимание на одну вещь, о которой не все знают:

  

ft @{l='Average';e={$_.Average};a='left'}

при форматировании Format-Table вы можете атрибутум A (от слова align) двигать содержимое колонки относительно названия. В первом примере у меня стандартное форматирование и выравнивание Avarage по правому краю. Здесь же я содержимое выравнил по левому краю, чтобы не сливались цифры. Хотя это решается удалением –AutoSize, но мне нравится этот ключ :) вобщем, об этой фиче не забывайте.

Tuesday, June 16, 2009 12:15:38 AM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Beginner Division

Summer Scripting Games 2009 задача на работу с реестром.

Задача:

  • Найти текущее значение максимального количества закачек в Internet Explorer
  • Увеличить это число на другое значение
  • при чтении этого значения предусмотреть проверку существования этого параметра

Решение:

Ключи и значения реестра для этой задачи можно найти тут: http://support.microsoft.com/kb/282402. В принципе, очень просто тут:

$path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings'
if (!(Test-Path $path)) {New-Item -ItemType Registry -Path $path -Force}
if (!(Get-ItemProperty $path).MaxConnectionsPer1_0Server) {
    Write-Warning "MaxConnectionsPer1_0Server property doesn't exist"
    [void](New-ItemProperty -Path $path -Name 'MaxConnectionsPer1_0Server' -Value 10 -PropertyType 'DWord')
} else {"MaxConnectionsPer1_0Server is: " + (Get-ItemProperty $path).MaxConnectionsPer1_0Server}
if (!(Get-ItemProperty $path).MaxConnectionsPerServer) {
    Write-Warning "MaxConnectionsPerServer property doesn't exist"
    [void](New-ItemProperty -Path $path -Name 'MaxConnectionsPerServer' -Value 10 -PropertyType 'DWord')
} else {"MaxConnectionsPerServer is: " + (Get-ItemProperty $path).MaxConnectionsPerServer}

и вот вывод:

[↓] [vPodans] $path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' [↓] [vPodans] if (!(Test-Path $path)) {New-Item -ItemType Registry -Path $path -Force} [↓] [vPodans] if (!(Get-ItemProperty $path).MaxConnectionsPer1_0Server) { >> Write-Warning "MaxConnectionsPer1_0Server property doesn't exist" >> [void](New-ItemProperty -Path $path -Name 'MaxConnectionsPer1_0Server' -Value 10 -PropertyType 'DWord') >> } else {"MaxConnectionsPer1_0Server is: " + (Get-ItemProperty $path).MaxConnectionsPer1_0Server} >> if (!(Get-ItemProperty $path).MaxConnectionsPerServer) { >> Write-Warning "MaxConnectionsPerServer property doesn't exist" >> [void](New-ItemProperty -Path $path -Name 'MaxConnectionsPerServer' -Value 10 -PropertyType 'DWord') >> } else {"MaxConnectionsPerServer is: " + (Get-ItemProperty $path).MaxConnectionsPerServer} >> WARNING: MaxConnectionsPer1_0Server property doesn't exist WARNING: MaxConnectionsPerServer property doesn't exist [↓] [vPodans] $path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' [↓] [vPodans] if (!(Test-Path $path)) {New-Item -ItemType Registry -Path $path -Force} [↓] [vPodans] if (!(Get-ItemProperty $path).MaxConnectionsPer1_0Server) { >> Write-Warning "MaxConnectionsPer1_0Server property doesn't exist" >> [void](New-ItemProperty -Path $path -Name 'MaxConnectionsPer1_0Server' -Value 10 -PropertyType 'DWord') >> } else {"MaxConnectionsPer1_0Server is: " + (Get-ItemProperty $path).MaxConnectionsPer1_0Server} >> if (!(Get-ItemProperty $path).MaxConnectionsPerServer) { >> Write-Warning "MaxConnectionsPerServer property doesn't exist" >> [void](New-ItemProperty -Path $path -Name 'MaxConnectionsPerServer' -Value 10 -PropertyType 'DWord') >> } else {"MaxConnectionsPerServer is: " + (Get-ItemProperty $path).MaxConnectionsPerServer} >> MaxConnectionsPer1_0Server is: 10 MaxConnectionsPerServer is: 10 [↓] [vPodans]

как бы ничего сложного.

Advanced Division

работа с тэгами файлов. Потребуются файлы TechEd1.JPG, TechEd2.JPG и TechEd3.JPG из Competitors Pack.

Задача:

  • прочитать следующие EXIF свойства файлов:
  • Имя
  • когда снимок был сделан
  • свойство Make
  • и модель камеры, которой был сделан снимок

Решение:

Задача не такая и сложная, на самом деле, как кажется. Для начала нам потребуется класс System.Drawing.Bitmap и его метод GetPropertyItem. Для работы с этим классом нужно подключить библиотеку System.Drawing.dll:

[void][reflection.assembly]::loadfile("C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll")

и создать объект System.Drawing.Bitmap:

$drawing = New-Object system.drawing.bitmap -ArgumentList .\TechEd1.jpg

Этот объект будет содержать всё нам необходимое. Чтобы посмотреть нужные нам свойства, нужно получить где-то список всех ID, которым соответствуют эти свойства. К сожалению я не нашёл такого списка на MSDN, а только отдельными частями в интернете:
http://www.exif.org/specifications.html

там в конце PDF файла есть Appendix с таблицой ID на эту тему. Если посмотреть в таблицу, то увидим, что Model находится под ID = 272. Глянем, что там есть:

[↓] [vPodans] $drawing = New-Object system.drawing.bitmap -ArgumentList .\TechEd1.jpg [↓] [vPodans] $drawing.GetPropertyItem(271).value 67 97 110 111 110 0 [↓] [vPodans]

Как-то не очень радует такой вывод. Но если внимательно посмотреть на эти цифры, то можно заметить, что они не превышают число 256, что подсказывает, что это массив ASCII байтов. Эти ASCII байты можно сконвертировать в элемент вот так: [char]ASCII_Number. А т.к. это массив, то его нужно собрать либо методом ToString() или оператором –Join, который есть в PowerShell V2:

[↓] [vPodans] $drawing = New-Object system.drawing.bitmap -ArgumentList .\TechEd1.jpg [↓] [vPodans] $drawing.GetPropertyItem(271).value 67 97 110 111 110 0 [↓] [vPodans] -join ($drawing.GetPropertyItem(271).value | %{[char]$_}) Canon [↓] [vPodans]

Вот так оно всяко лучше стало. По такой же схеме выбираем и собираем все остальные свойства:

[void][reflection.assembly]::loadfile("C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll")
dir *.jpg | %{
    $drawing = New-Object system.drawing.bitmap -ArgumentList $_
    $photo = "" | Select Name, Author, Make, Model, DateTaken
    $photo.Name = $_.Name
    $photo.Author = -join ($drawing.GetPropertyItem(315).value | %{[char]$_})
    $photo.Make = -join ($drawing.GetPropertyItem(271).value | %{[char]$_})
    $photo.Model = -join ($drawing.GetPropertyItem(272).value | %{[char]$_})
    $photo.DateTaken = -join ($drawing.GetPropertyItem(36867).value | %{[char]$_})
    $photo
} | ft -AutoSize

[↓] [vPodans] [void][reflection.assembly]::loadfile("C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll") [↓] [vPodans] dir *.jpg | %{ >> $drawing = New-Object system.drawing.bitmap -ArgumentList $_ >> $photo = "" | Select Name, Author, Make, Model, DateTaken >> $photo.Name = $_.Name >> $photo.Author = -join ($drawing.GetPropertyItem(315).value | %{[char]$_}) >> $photo.Make = -join ($drawing.GetPropertyItem(271).value | %{[char]$_}) >> $photo.Model = -join ($drawing.GetPropertyItem(272).value | %{[char]$_}) >> $photo.DateTaken = -join ($drawing.GetPropertyItem(36867).value | %{[char]$_}) >> $photo >> } | ft -AutoSize >> Name Author Make Model DateTaken ---- ------ ---- ----- --------- TechEd1.JPG Ed Wilson Canon Canon PowerShot G9 2009:05:12 10:41:03 TechEd2.JPG Ed Wilson Canon Canon PowerShot G7 2009:05:12 10:25:59 TechEd3.JPG Ed Wilson Sony A-9 2009:05:12 10:26:48 [↓] [vPodans]

Вобщем, как видите, тут тоже ничего сверхсложного нету.

Sunday, June 14, 2009 1:16:39 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Summer Scripting Games 2009 Прыжки с шестом.

Beginner Division

Потребуется файл HighJumperDatabase.mdb из Competitors Pack.

Задача:

  • опросить MDB файл и вывести имя лучшего прыгуна c шестом (хотя там цифры немного странные для этой дисциплины).

Решение:

Никогда не приходилось работать с Access, поэтому пришлось честно гуглить: http://www.visualbasicscript.com/m_43134/tm.htm. Его я взял за основу, только немного подрихтовал и вот что получилось:

# создаём массив, который будет содержать все результаты
$sum = @()
# подключения к БД. Наглый копипаст с форума.
# более-менее внятное описание этого кода тут: # http://technet.microsoft.com/en-us/magazine/2009.05.scriptingguys.aspx $adOpenStatic = 3 $adLockOptimistic = 3 $MDBConn = New-Object -comobject ADODB.Connection $MDBConn.Open("provider=microsoft.jet.oledb.4.0;data source=$pwd\HighJumperDatabase.mdb") $Record = New-Object -comobject ADODB.RecordSet $Record.Open('select * from [High Jumper Data]',$MDBConn,$adOpenStatic,$adLockOptimistic) $Record.MoveFirst() do { # делаем объект текущего атлета и забиваем в него нужные значения $current = "" | Select @{n='Name';e={$Record.Fields.Item("Name").Value}}, @{n='Personal Best';e={$Record.Fields.Item("Personal Best").Value}}, @{n='Season Best';e={$Record.Fields.Item("Season Best").Value}} # добавляем текущего участника в массив $sum += $current # переходим на следующую итерацию $Record.MoveNext() } until ($Record.EOF) # закрываем все подключения $Record.Close() $MDBConn.Close() # сортируем и выводим лучшего спортсмена $sum | Sort "Season Best", "Personal Best" | select Name -Last 1

Кстати, отличное решение этой задачи нашёл на VBS. Человек уложился в 4(!) строчки:

Set adoCon = CreateObject("ADODB.Connection")
adoCon.Open "DRIVER=Microsoft Access Driver (*.mdb);DBQ=HighJumperDatabase.mdb"
Winner = adoCon.Execute("SELECT TOP 1 Name FROM [High Jumper Data] ORDER BY [Personal Best] DESC")("Name")
WScript.Echo "Expected Winner: " & Winner

Есть мнение, что фанаты PowerShell должны дружно застрелиться :) хотя это просто вопрос запроса к базе Access.

Advanced Division

потребуется файл High Jump Stats_Adv4.txt из Competitors Pack.

В файле перечислены имена прыгунов с шестом и через запятую перечислены высОты планки.

Задача:

  • на основании этих данных построить произвольный (желательно красивы) график в Excel.

Решение:

Т.к. я графики тоже никогда не рисовал, поэтому тоже пришлось обратиться к гуглу. И вышло примерно такое:

# переименовываем txt файл в CSV. Теперь не нужно конвертировать тектовый файл в
# формат Excel, т.к. при переименовании Excel сам это сделает
ren "High Jump Stats_Adv4.txt" "High Jump Stats_Adv4.csv"
# выполняем обычную рутину для создания COM объекта для Excel
$excel = New-Object -ComObject excel.application
$file = $excel.Workbooks.Open("$pwd\High Jump Stats_Adv4.csv")# | Out-Null
$excel.Visible = $false
$excel.DisplayAlerts = $false
$sheet = $excel.Worksheets.Item(1)
# добавляем диаграмму
#$chart = $excel.Charts.Add()
# задаём диапазон для диаграммы
$Range = $sheet.Range("A1:l1")
# добавляем элемент Chart с нужным диапазоном. Я выбрал 10 первых попыток
# для первого атлета. 
$chart = $excel.Charts.Add()
$chart.SetSourceData($Range)
$chart.ChartStyle = 27
# сохраняем Chart в файл формата Excel и закрываем работу
$file.SaveAs("$pwd\High Jump Stats_Adv4.xlsx")
$excel.Quit()
gps excel | stop-process
# переименовываем CSV файл обратно в TXT
ren "High Jump Stats_Adv4.csv" "High Jump Stats_Adv4.txt"
Sunday, June 14, 2009 12:17:31 AM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Summer Scripting Games 2009 3-е задание на парсинг текста.

Beginner Division

Потребуется файл Shot Put.txt из Competitors Pack.

Задача:

  • В файле есть 2 абзаца. Нужно первый абзац записать в файл Shot Put A.txt, а второй абзац записать в Shot Put B.txt.
  • Переименовать файл Shot Put.txt в Shot Put.old.

Решение:

Если внимательно посмотреть на файл, то в нём можно найти одну уникальную последовательность - `r`n`r`n. Она разделяет 2 абзаца. Поэтому эту последовательность можно использовать в качестве разделителя для Get-Content:

[↓] [vPodans] $file = gc '.\Shot Put.txt' -Delimiter "`r`n`r`n" [↓] [vPodans] $file[0] This is the first paragraph of the shot put text file. It will need to be placed into the first text file which you will create. The shot put is an event involving putting and heavy metal ball. The object of the event is to throw the put as far as possible, or to put the shot : anyway, you get the idea. [↓] [vPodans] $file[1] This is the second paragraph of the shot put text file. it contains information you will need to put into the second file which you will also create. [↓] [vPodans]

Если со вторым абзацем понятно, то у первого очень много пробелов и пустых строк. Их можно удалить при помощи метода Trim(), который использовался уже в Event1. Собственно, само решение:

$file = gc '.\Shot Put.txt' -Delimiter "`r`n`r`n" | %{$_.trim()}
$file[0] > 'Shot Put A.txt'
$file[1] > 'Shot Put B.txt'
ren '.\Shot Put.txt' 'Shot Put.old'

Advanced Division

потребуется файл Wordlist_ADV3.txt из Competitors Pack.

Задача:

  • распарсить текстовый файл и вытащить из них слова, в которых используется не более одной уникальной гласной буквы.
  • записать эти слова в новый текстовый файл.

Решение:

В принципе, тут есть только одно простое решение (считайте, что нагло стырено у Васи):

gc .\Wordlist_ADV3.txt | ?{$_ -replace '[^AEIOUY]' -match '^(.)\1*$'} > 'Wordlist_ADV3_new.txt'

суть решения заключается в том, чтобы из каждого слова убрать все согласные и проверить, что в слове используется только одна гласная 1 или более раз. Этот регексп описан в PowerShell Cookbook на странице 494.

Friday, June 12, 2009 10:07:50 AM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Beginner Division

Summer Scripting Games 2009 Тут очень простое задание из серии фаллометрии :)

Задача:

  • Показать данные своего процессора (процессоров) в соответствии с рисунком

Решение:

Тут нам потребуется WMI класс Win32_Processor. И из него выберем нужные данные. При этом я проверю, что значения не равны $null или нулю. Если это так, то в это поле впишу текст N/A для читабельности. Вот что получилось:

$Host.UI.RawUI.BackgroundColor = "Black"
cls
$processor = gwmi win32_processor | Select AddressWidth, L2CacheSize, L2CacheSpeed, L3CacheSize,
L3CacheSpeed, MaxClockSpeed, Name, NumberOfCores, NumberOfLogicalProcessors
$processor | gm -MemberType NoteProperty | %{
    if ($processor.$($_.name) -eq $null -or $processor.$($_.name) -eq 0) {$processor.$($_.name) = "N/A"}
}
Write-Host 'Strength evaluation for LocalHost' -ForegroundColor Green
Write-Host 'Speed ... ' $processor.MaxClockSpeed `
`n'L2 Cache Size:' $processor.L2CacheSize `
`n'L2 Cache Speed:' $processor.L2CacheSpeed `
`n'L3 Cache Size:' $processor.L3CacheSize `
`n'L3 Cache Speed:' $processor.L3CacheSpeed -ForegroundColor Yellow
Write-Host Strength ... `
`n'Number of Cores:' $processor.NumberOfCores `
`n'Number of Logical Processors:' $processor.NumberOfLogicalProcessors `
`n'Name: `t`t`t`t' $processor.Name -ForegroundColor Magenta
Write-Host Agility ... `
`n'Address Width:' $processor.AddressWidth -ForegroundColor Cyan

И вот вывод:

[↓] [vPodans] $processor = gwmi win32_processor | Select AddressWidth, L2CacheSize, L2CacheSpeed, L3CacheSize, >> L3CacheSpeed, MaxClockSpeed, Name, NumberOfCores, NumberOfLogicalProcessors >> $processor | gm -MemberType NoteProperty | %{ >> if ($processor.$($_.name) -eq $null -or $processor.$($_.name) -eq 0) {$processor.$($_.name) = "N/A"} >> } >> Write-Host 'Strength evaluation for LocalHost' -ForegroundColor Green >> Write-Host 'Speed ... ' $processor.MaxClockSpeed ` >> `n'L2 Cache Size:' $processor.L2CacheSize ` >> `n'L2 Cache Speed:' $processor.L2CacheSpeed ` >> `n'L3 Cache Size:' $processor.L3CacheSize ` >> `n'L3 Cache Speed:' $processor.L3CacheSpeed -ForegroundColor Yellow >> Write-Host Strength ... ` >> `n'Number of Cores:' $processor.NumberOfCores ` >> `n'Number of Logical Processors:' $processor.NumberOfLogicalProcessors ` >> `n'Name: $processor.Name -ForegroundColor Magenta >> Write-Host Agility ... ` >> `n"Address Width: `t`t`t`t" $processor.AddressWidth -ForegroundColor Cyan >> Strength evaluation for LocalHost Speed ... 2834 L2 Cache Size: 6144 L2 Cache Speed: 2000 L3 Cache Size: N/A L3 Cache Speed: N/A Strength ... Number of Cores: 4 Number of Logical Processors: 4 Name: Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz Agility ... Address Width: 64 [↓] [vPodans]

По всей видимости оно получилось как и требовалось.

Advanced Division

И тут нас снова ждут соревнования. Здесь нам потребуется файл LongJump_Adv2.xls из Competitors Pack. Файл содержит данные об участниках прыжков в длину и результаты их 3-х попыток.

Задача:

  • На основании результатов 3-х попыток в колонку Result записать лучший результат из 3-х попыток
  • На основании результатов третей попытки для каждого участника, выбрать лучший результат из них и вывести на экран
  • Сравнить лучший результат из 3-х попыток для каждого участника с их средним результатом в сезоне. Такой графы я не нашёл, есть только лучший результат в сезоне. Вот с ним я и сравнивал. Если прыгун превзошёл свой личный рекорд сезона, то в графу Exceed,Achieve,Under Perform вписать Exceed. Если повторил, то Achieve или Under Perform, если лучший результат сегодня был хуже результата по сезону.
  • Отсортировать всю таблицу по графе Exceed,Achieve,Under Perform именно в таком порядке. Т.е. сперва идут те, кто улучшил свой показатель, потом – кто повторил и в самом конце те, кто не смог повторить свой результат в сезоне. И сохранить в файл с таким же именем.

Решение:

Вася Гусев решил окончательно сломать всем мозг разбором XLS в CSV, обработать его и собрать обратно в CSV – тиснуть сюда. Когда я это увидел – стало как-то не очень хорошо. Я решил работать прямо с XLS. И вот что у меня получилось:

# массив, который будет содержать результаты последней попытки прыжков, на основании
# которых нужно вывести лучший результат
$lastattempt = @()
# создаём COM объект и прочую рутину.
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false
$excel.Workbooks.Open('C:\Users\vPodans\LongJump_Adv2.xls') | Out-Null
# у нас есть известное кол-во участников - 18. 19-я строка - это заголовки.
# поэтому я исходя из этого решил работать с колонкой Result (8-я по счёту). Т.к.
# некоторые попытки были провалены и в таблице стояли крестики. Чтобы вручную не парсить
# это всё, я в ячейку воткнул экселовскую формулу MAX, которая всё за меня сделает.
2..19 | %{$excel.Cells.Item($_,8).Value2 = "=max(e$_`:g$_)"
    # заодно в $lastattempt запишем результат последней попытки для текущего прыгуна
    $lastattempt += $excel.Cells.Item($_,7).Value2
    # сравниваем лучший результат за сегодня с результатом за сезон
    if ($excel.Cells.Item($_,8).Value2 -lt $excel.Cells.Item($_,4).Value2) {
    # и в соответствующую графу пишем результат
        $excel.Cells.Item($_,9).Value2 = "Under Perform"
    # вот тут пришлось использовать читы. Я не очень силён в custom sort в экселе, поэтому
    # рядышком я для каждого статуса Exceed/Achieve/.. я приписываю число. Вот по этому числу
    # я потом буду сортировать таблицу
        $excel.Cells.Item($_,10).Value2 = "3"
    } elseif ($excel.Cells.Item($_,8).Value2 -eq $excel.Cells.Item($_,4).Value2) {
        $excel.Cells.Item($_,9).Value2 = "Achieve"
        $excel.Cells.Item($_,10).Value2 = "2"
    } else {
        $excel.Cells.Item($_,9).Value2 = "Exceed"
        $excel.Cells.Item($_,10).Value2 = "1"
    }
}
# выбираем из LastAttempt лучший результат, попутно заменяя крестики на нолики (лол) и выводя лучший результат
$lastattempt | %{if ($_ -eq "x") {$_ = 0};$_} | sort | select -last 1
# выбираем диапазон изменения таблицы при сортировке. Заголовки не трогаем, поэтому начинаем с 2
$selection = $excel.Range("a2:j19")
# указываем колонку, по которой будем сортировать
$range = $excel.Range("j2:j19")
# сортируем
[void]$selection.Sort($range,"1")
# удаляем читерские числа, которые использовались для сортировки.
2..19 | %{$excel.Cells.Item($_,10).Value2 = ""}
# готово.
$excel.Save()
$excel.Quit()

Не скажу, что получилось коротко и идеально, но задачу вполне решает.

Thursday, June 11, 2009 10:00:41 AM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Summer Scripting Games 2009 И оно свершилось! Хотя официально Scripting Games начинаются только 15-го июня, но уже со вчерашнего дня стали известны уже 2 задания. Для начала немного информации, что и как:

В принципе, вы можете решать задания как угодно, т.к. засчитываются все, которые отвечают условиям задания.

Итак, Event1 и оба дивизиона:

Beginner Division

Из Competitors Pack нам потребуется файл 100 Meter Event.txt

Задача:

  • Выяснить, как читать файл?
  • Как прочитать из каждой строки поля Name, Country, Time?
  • Как отсортировать содержимое файла по полю Name?
  • Выбрать 3-х победителей из списка. Победителем считается тот, у кого самое маленькое время в колонке Time
  • Показать поля Name, Country и Time для первых 3-х победителей.

Решение:

Побегав курсором по файлу я обнаружил тот факт, что разделителем между столбцом Name и Country является табулятор ([Tab]), а между Country и Time стоит один пробел во второй строке, а в остальных строках стоит [Tab]. Следовательно, варианты использования командлетов с параметром –Delimeter не прокатят и нужно будет разбирать всё регулярными выражениями. Первый вопрос наталкивает на необходимость чтения файла не через Get-Content, а через [system.io.file]::ReadAllText(). Но я не вижу особого криминала в использовании родного для PowerShell командлета Get-Content. Это будет несложно. Основная проблема в том, как составить регулярное выражение. Я решил разобрать имя на First Name и LastName (т.к. правильней, всё же, будет, когда сначала идёт имя, а потом фамилия), далее выбрать Country от табуляции до первого числа. Причём, тут потребуется каждую часть сделать именованной (named capture). Если открыть PowerShell In Action на 111 странице, то там можно найти описание, как делать named captures. У меня регулярное выражение получилось вот такое:

^(?<ln>\w+)..(?<fn>.*)\t(?<country>.+)(?<time>\d\.\d+)

Что делает это выражение:

  • в секцию LN (LastName) попадают все буквы до первого небуквенного знака.
  • далее пропускаем 2 символа (запятую и пробел).
  • в секцию FN (FirstName) попадает всё до первой табуляции.
  • после табуляции идёт Country и в него запишем всё, вплоть до первого числового знака.
  • И числа записываем в секцию Time.

Давайте проверим его:

[↓] [vPodans] $file = gc '.\100 Meter Event.txt' [↓] [vPodans] $file[1] -match "^(?<ln>\w+)..(?<fn>.*)\t(?<country>.+)(?<time>\d\.\d+)" True [↓] [vPodans] $matches Name Value ---- ----- country Australia ln Aaberg fn Jesper time 8.57 0 Aaberg, Jesper Australia 8.57 [↓] [vPodans] $file[11] -match "^(?<ln>\w+)..(?<fn>.*)\t(?<country>.+)(?<time>\d\.\d+)" True [↓] [vPodans] $matches Name Value ---- ----- country Japan ln Hansen fn Anne Grethe time 8.85 0 Hansen, Anne Grethe Japan 8.85 [↓] [vPodans]

и теперь соберём объект с нужными свойствами и отправим его на выход. На выходе отсортируем объекты по параметру Time и выберем первые 3 объекта:

gc "100 Meter Event.txt" | ?{$_ -match "^(?<ln>\w+)..(?<fn>.*)\t(?<country>.+)(?<time>\d\.\d+)"} |%{
    $obj = "" | Select @{n='Name';e={$matches.fn + " " + $matches.ln}},
    @{n='Country';e={$matches.country}},@{n='Time';e={$matches.time}}
    $obj
} | sort time | select -First 3 | ft -AutoSize

и вот вывод:

[↓] [vPodans] gc "100 Meter Event.txt" | ?{$_ -match "^(?\w+)..(?.*)\t(?.+)(?

решил ещё отформатировать в Format-Table с ключом –AutoSize для красоты. Признаюсь, что это задание меня сильно озадачило. Для меня оно оказалось очень непростым (такие дела).

Advanced Division

Для этой задачи нам потребуется файл Personal Information Cards_ADV1.txt из Competitors Pack.

Задача:

  • найти самую короткую строку в файле, которая содержит какой-либо текст.
  • вывести на экран 3 самые короткие строчки

Решение:

В файле много пустых строк, но это не проблема. Итак, one-liner решение:

gc "Personal Information Cards_ADV1.txt" | ?{$_.trim().length -ne 0} | sort length | select -First 3

[↓] [vPodans] gc "Personal Information Cards_ADV1.txt" | ?{$_.trim().length -ne 0} | select –First 3 PPID Claims Street [↓] [vPodans]

Странно, что в Advanced Division получилось такое простое задание.

Wednesday, June 10, 2009 3:16:53 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Работая с функциями в PowerShell можно столкнуться с одной особенностью – в качестве передачи аргументов в функции или скрипты вы можете использовать почти всё, кроме ключей (данных типа Switch). Их передавать можно, но тут есть одна особенность. Обычно это ощущается, когда вы работаете с командлетами. Возьмём простой пример:

function Test ([string[]]$Path, [String]$Filter, [switch]$Force) {
Write-Host '$Path is:' $Path
Write-Host '$Filter is:' $Filter
Write-Host '$Force is:' $Force
}

и вызовем эту функцию:

[↓] [vPodans] function Test ([string[]]$Path, [String]$Filter, [switch]$Force) { >> Write-Host '$Path is:' $Path >> Write-Host '$Filter is:' $Filter >> Write-Host '$Force is:' $Force >> } >> [↓] [vPodans] Test C:\ * -force $Path is: C:\ $Filter is: * $Force is: True [↓] [vPodans]

В принципе, всё как и ожидалось. Но если внимательно посмотреть на последний аргумент, то мы увидим лишь True, т.е. увидим факт, что ключ Force был передан. Однако, PowerShell не умеет подставять (биндить) переменную $Force (равно как и другие переменные) как именованный параметр в другую команду. Чтобы в этом убедиться, мы попробуем сымитировать нашу функцию как командлет Get-ChildItem:

[↓] [vPodans] function Test ([string[]]$Path, [String]$Filter, [switch]$Force) { >> Get-ChildItem $Path $Filter $Force >> } >> [↓] [vPodans] Test C:\ * -Force Get-ChildItem : A positional parameter cannot be found that accepts argument 'True'. At line:2 char:14 + Get-ChildItem <<<< $Path $Filter $Force + CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand [↓] [vPodans]

Как видно, командлет Get-ChildItem не смог сопоставить последний аргумент ни с одним из своих параметров. Т.е. по факту выполнилась следующая строка:

Get-ChildItem -Path C:\ -Filter * True

PowerShell не смог сказать командлету, что мы указали ключ Force и хотим именно его передать в командлет. По факту в этом коде Get-ChildItem не знал, какие параметры ему были переданы и подставлял их на основе номера позиции параметра. А т.к. у Get-ChildItem нету параметра с порядковым номером 3 (с номером 1 идёт Path или LiteralPath, а с номером 2 идёт Filter. Остальные параметры именованные), то мы получили ошибку. Но всё же, как выкручиваться из этой ситуации? Вы можете как угодно пытаться подставить аргументы, но ничего не выйдет. Для этих целей в PowerShell V2 появилась специальная переменная - $PSBoundParameters. Эта переменная по сути представляет собой хэш-таблицу:

[↓] [vPodans] function Test ([string]$Path, [String]$Filter, [switch]$Force) { >> $PSBoundParameters >> } >> [↓] [vPodans] Test C:\ * -Force Key Value --- ----- Force True Path C:\ Filter * [↓] [vPodans]

В отличии от первого примера переменная $PSBoundParameters содержит не только значения переменных, но и их имена (в перовм примере я вручную дописывал имена переменных), которые используются в качестве именованных параметров. Т.е. при подстановке аргументов в команду, она сначала выбирает имя переменной в качестве именованного параметра и значение переменной подставляет в качестве аргумента этого параметра. Синтаксис использования этой переменной очень прост:

function Name ($arg1, $arg2, $arg3 ... $argN) {
Command @PSBoundParameters
}

и в результате будет исполняться вот такая команда:

Command –arg1 <значение $arg1> –arg2 <значение $arg2> –arg3 <значение $arg3> … –argN <значение $argN>

Т.е. будут подставляться именованные параметры и значения переменных соответствующих аргументов. Давайте проверим, как это подействует на наш пример с Get-ChildItem:

function Test ([string]$Path, [String]$Filter, [switch]$Force) {
Get-ChildItem @PSBoundParameters
}

[↓] [vPodans] function Test ([string]$Path, [String]$Filter, [switch]$Force) { >> Get-ChildItem @PSBoundParameters >> } >> [↓] [vPodans] Test C:\ * -Force Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d--hs 10.07.2007. 19:44 $Recycle.Bin d--hs 09.03.2008. 17:53 Boot d---- 23.11.2008. 19:41 inetpub d-rh- 26.06.2007. 22:27 MSOCache d---- 09.03.2008. 17:41 PerfLogs <...>

И вы можете видеть, что у нас всё получилось в наилучшем виде! В сравнении с предыдущим примером фактически выполнилась следующая команда:

Get-ChildItem -Path C:\ -Filter * -Force

Для формирования аргументов для командлетов в функциях (так называемые wrapped cmdlets) это самый красивый и идеальный вариант. Если вы захотите изменить логику стандартных командлетов или добавить в них свой функционал, то $PSBoundParamters сделает за вас очень много лишней работы. Но это не единственное полезное применение для этой переменной. Она так же позволяет сократить возможность ошибки при вызове функции внутри скрипта или другой функции, которая принимает те же аргументы. Давайте посмотрим ещё один пример:

function Test ([string[]]$Path, [String]$Filter, [switch]$Force) {
    Write-Host '$Path in Test is:' $Path
    Write-Host '$Filter in Test is:' $Filter
    Write-Host '$Force in Test is:' $Force
    Write-Host --------------------------------
    function Test2 ([switch]$Force, [string[]]$Path, [String]$Filter) {
Write-Host '$Path in Test2 is:' $Path
Write-Host '$Filter in Test2 is:' $Filter Write-Host '$Force in Test2 is:' $Force } Test2 @PSBoundParameters }

Что мы делаем: мы создали функцию Test, которая принимает набор аргументов. Внутри этой функции есть другая функция Test2, которая принимает те же аргументы. Далее из функции Test вызываем функцию Test2 и с помощью $PSBoundParameters передаём в неё аргументы. Обратите внимание, что в функции Test2 я изменил порядок аргументов. Это сделано для того, чтобы показать, что $PSBoundParamters отсортирует наши аргументы. А теперь внимание на экран:

[↓] [vPodans] function Test ([string[]]$Path, [String]$Filter, [switch]$Force) { >> Write-Host '$Path in Test is:' $Path >> Write-Host '$Filter in Test is:' $Filter >> Write-Host '$Force in Test is:' $Force >> Write-Host -------------------------------- >> function Test2 ([switch]$Force, [string[]]$Path, [String]$Filter) { >> Write-Host '$Path in Test2 is:' $Path >> Write-Host '$Filter in Test2 is:' $Filter >> Write-Host '$Force in Test2 is:' $Force >> } >> Test2 @PSBoundParameters >> } >> [↓] [vPodans] Test C:\ * -Force $Path in Test is: C:\ $Filter in Test is: * $Force in Test is: True -------------------------------- $Path in Test2 is: C:\ $Filter in Test2 is: * $Force in Test2 is: True [↓] [vPodans]

И смотрите, что у нас получилось. А у нас получилось, что функция Test2 получила тот же набор аргументов, причём они были подставлены в правильном соответствии. Фактически строка Test2 @PSBoundParameters была преобразована в:

Test2 -Path $Path -Filter $Filter -Force $true

Т.е. в вариант, который мы вынуждены использовать в PowerShell 1.0. Плюс, мы имеем возможность подставлять позиционно ключи (объекты типа Switch), что есть очень позитивно.

Tuesday, June 09, 2009 12:41:52 AM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Загрыз меня сегодня один очень важный вопрос, как мне кажется, вселенского масштаба, а именно – шрифт оформления кода на страницах блога. И, как вы уже смогли заметить, что у меня весь код в блоге оформлен шрифтом Verdana, что является моветоном (Verdana – не моноширинный шрифт). В принципе, ни для кого не секрет, что любой код должен оформляться только моноширинными шрифтами. И я решил проверить, на сколько это будет хорошо выглядеть. Поэтому я предлагаю вам на обозрение по 10 строк произвольного кода раскрашенного в PowerGUI, но оформленного разными шрифтами:

  • Verdana (используется сейчас в блоге)
if ($name -ne $null) {
$shares = Get-WmiObject Win32_Share -filter "name = '$name'"
} Else {$shares = Get-WmiObject Win32_Share -filter 'type = 0'}
$Shareinfo = @()
# цикл извлечения сведений о каждой сетевой папке в переменную $ShareInfo
foreach ($share in $shares) { 
  $ShareSec = Get-WmiObject Win32_LogicalShareSecuritySetting  -filter "name='$($share.name)'" 
  if($shareSec) { 
    $sd = $sharesec.GetSecurityDescriptor()
    $ShareInfo += $SD.Descriptor.DACL | % {
      $_ | select @{e = {$share.name};n = 'Name'},
  • Consolas
if ($name -ne $null) {
$shares = Get-WmiObject Win32_Share -filter "name = '$name'"
} Else {$shares = Get-WmiObject Win32_Share -filter 'type = 0'}
$Shareinfo = @()
# цикл извлечения сведений о каждой сетевой папке в переменную $ShareInfo
foreach ($share in $shares) { 
  $ShareSec = Get-WmiObject Win32_LogicalShareSecuritySetting  -filter "name='$($share.name)'" 
  if($shareSec) { 
    $sd = $sharesec.GetSecurityDescriptor()
    $ShareInfo += $SD.Descriptor.DACL | % {
      $_ | select @{e = {$share.name};n = 'Name'},
  • Courier
if ($name -ne $null) {
$shares = Get-WmiObject Win32_Share -filter "name = '$name'"
} Else {$shares = Get-WmiObject Win32_Share -filter 'type = 0'}
$Shareinfo = @()
# цикл извлечения сведений о каждой сетевой папке в переменную $ShareInfo
foreach ($share in $shares) { 
  $ShareSec = Get-WmiObject Win32_LogicalShareSecuritySetting  -filter "name='$($share.name)'" 
  if($shareSec) { 
    $sd = $sharesec.GetSecurityDescriptor()
    $ShareInfo += $SD.Descriptor.DACL | % {
      $_ | select @{e = {$share.name};n = 'Name'},
  • Courier New (используется по умолчанию в PowerGUI)
if ($name -ne $null) {
$shares = Get-WmiObject Win32_Share -filter "name = '$name'"
} Else {$shares = Get-WmiObject Win32_Share -filter 'type = 0'}
$Shareinfo = @()
# цикл извлечения сведений о каждой сетевой папке в переменную $ShareInfo
foreach ($share in $shares) { 
  $ShareSec = Get-WmiObject Win32_LogicalShareSecuritySetting  -filter "name='$($share.name)'" 
  if($shareSec) { 
    $sd = $sharesec.GetSecurityDescriptor()
    $ShareInfo += $SD.Descriptor.DACL | % {
      $_ | select @{e = {$share.name};n = 'Name'},
  • Courier 10TL
if ($name -ne $null) {
$shares = Get-WmiObject Win32_Share -filter "name = '$name'"
} Else {$shares = Get-WmiObject Win32_Share -filter 'type = 0'}
$Shareinfo = @()
# цикл извлечения сведений о каждой сетевой папке в переменную $ShareInfo
foreach ($share in $shares) { 
  $ShareSec = Get-WmiObject Win32_LogicalShareSecuritySetting  -filter "name='$($share.name)'" 
  if($shareSec) { 
    $sd = $sharesec.GetSecurityDescriptor()
    $ShareInfo += $SD.Descriptor.DACL | % {
      $_ | select @{e = {$share.name};n = 'Name'},
  • Lucida Console (используется по умолчанию в PowerShell ISE)
if ($name -ne $null) {
$shares = Get-WmiObject Win32_Share -filter "name = '$name'"
} Else {$shares = Get-WmiObject Win32_Share -filter 'type = 0'}
$Shareinfo = @()
# цикл извлечения сведений о каждой сетевой папке в переменную $ShareInfo
foreach ($share in $shares) { 
  $ShareSec = Get-WmiObject Win32_LogicalShareSecuritySetting  -filter "name='$($share.name)'" 
  if($shareSec) { 
    $sd = $sharesec.GetSecurityDescriptor()
    $ShareInfo += $SD.Descriptor.DACL | % {
      $_ | select @{e = {$share.name};n = 'Name'},

И мне хотелось бы получить от вас ответ – какой из этих шрифтов является наиболее читабельным и удобным для вас? Очень большая просьба отписаться в каментах со своим ответом. Спасибо!

Monday, June 08, 2009 12:02:29 AM (FLE Daylight Time, UTC+03:00)   Comments [5]    

 

Hal Rottenberg и Shay Levy сегодня собрали список блогов всех действующих MVP по PowerShell. Думается, что это будет весьма полезный для многих список, в котором любители PowerShell могут массу наиполезнейшей информации. Итак, вот список:

MVP Name

Blog Name

Blog language

/\/\o\/\/ ThePowerShellGuy

English

Arnaud Petitjean PowerShell Scripting

French

Brandon Shell BS on PoSH

English

Charlie Russel x(perts)64

English

Daisuke Mutaguchi Scripting Weblog

Japanese

Dmitry Sotnikov PowerShell and beyond

English

Don Jones Concentrated Technology

English

Doug Finke Development in a Blink

English

Gu Huajun ghjconan's blog

Chinese

Guy Thomas  

English

Hal Rottenberg TechProsaic

English

Hiroki Takahashi HIRO’s.NET

Japanese

Hiroshi Yoshioka PowerShell Memo

Japanese

Jeffrey Hicks Scripting Blog and More

English

Karl Prosser Live PowerShell

English

Keith Hill Keith Hill’s Blog

English

Kirk Munro Poshoholic

English

Marco Shaw Get-PowershellBlog

English

Max Trinidad Florida PowerShell User Group

English

Oisin Grehan Nivot Ink

English

Richard Siddaway Richard Siddaway’s Weblog

English

Shay Levy $cript Fanatic

English

Sherif Talaat The Arabian PowerShell

Arabic

Tobias Weltner Dreaming in PowerShell

English

Vadims Podans PowerShell Powered

Russian

Vasily Gusev PowerShell и другие скрипты

Russian

Vinicius Canto e a arte de criar scripts

Portuguese

Ying Li PowerShell & System Center

English

Удачи! © One

Friday, June 05, 2009 12:43:12 AM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Иногда замечаю, что некоторые скриптописатели получают “поломанный” конвейер, когда данные из одной команды вышли, но никуда не пришли. Возьмём простой пример:

[↓] [vPodans] foreach ($n in 1..3) {$n} 1 2 3 [↓] [vPodans]

Тут всё просто, циклом foreach перебираются данные и выводятся на экран. Мы привыкли, что всё, что выводится на экран можно передать дальше на конвейер. В принципе, это правильно, за исключением ряда случаев, когда это не работает. Чтобы убедиться в этом – передадим вывод этой команды через конвейер на Set-Content:

[↓] [vPodans] foreach ($n in 1..3) {$n} 1 2 3 [↓] [vPodans] foreach ($n in 1..3) {$n} | Set-Content file.txt An empty pipe element is not permitted. At line:1 char:28 + foreach ($n in 1..3) {$n} | <<<< Set-Content file.txt + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : EmptyPipeElement [↓] [vPodans]

и мы получаем ошибку, что у нас пустой конвейер. Если написать простой проверочный фильтр:

filter Test-Input {'current $_ is: ' + $_}

и проверить, что у нас приходит с конвейера в этот фильтр, то вы получите такую же ошибку и можете долго гадать, почему так, на экране данные есть, а в конвейер ничего не поступает. Причиной этому является то, что flow control (не знаю, как это на русский первести) конструкции не транслируют свой вывод в конвейер и вот список этих конструкций:

  • If…Else
  • If…ElseIf
  • For
  • Foreach
  • While
  • Do…While
  • Do…Until
  • Switch

Чтобы при использовании этих конструкций вывод транслировался на конвейер их нужно заключить в подвыражение – $( ):

[↓] [vPodans] $(foreach ($n in 1..3) {$n}) | Set-Content file.txt [↓] [vPodans] gc file.txt 1 2 3 [↓] [vPodans] $(foreach ($n in 1..3) {$n}) | Test-Input current $_ is: 1 current $_ is: 2 current $_ is: 3 [↓] [vPodans]

я заключил цикл Foreach в подвыражение и теперь данные стали поступать на конвейер. Это хорошо видно по выводу фильтра Test-Input, который приведён выше. Вот таким нехитрым способом мы решили проблему пустых конвейеров :)

Monday, June 01, 2009 8:25:14 PM (FLE Daylight Time, UTC+03:00)   Comments [2]    

 

 · 
All content © 2008 - 2010, Vadims Podāns
"Spaces" Theme provided by: Vadims Podāns
About


E-mail - Send mail to the author(s)
Live Messenger -
My former blog -
For english language visitors

Translate via Google Translator

Библиотека
Календарик
<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

Карта расположения посетителей
Favorites

Домашняя страничка Теры Патрик

Disclaimer
Вся информация на сайте предоставляется на условиях «как есть», без предоставления каких-либо гарантий и прав.

При использовании материалов c данного сайта ссылка на оригинальный источник обязательна.