sobota, 22 listopada 2014

Oculus i przyszłość gier

Jak wiesz jestem fanem gry Skyrim i dzisiaj wpadłem na filmik z oculusem w roli głównej.



Filmik przedstawia możliwości Virtualizer'a. Bieżnia została zaprojektowana przez grupę studentów z Wiednia. Jest też inny produkt do wirtualnej rzeczywistości (Virtix Omni):






Może kiedyś w przyszłości będę na LAN party, w wielkiej hali, gdzie będzie można grac w gry Counters-Strik czy Skyrim na tylko na takich maszynach..

czwartek, 20 listopada 2014

W końcu będzie null-conditional operator w C# 6

3 dni temu obejrzałem filmik z nowymi featuresami C#. Jest, w końcu, dziękuję. Wiem, że c# jest bardzo dynamicznie rozwijanym językiem, ale oczekiwałem ... może od kiedy zacząłem programować obiektowo, bezpiecznej nawiagacji po polach nie zważając na null'e. Odkąd ludzie wymyślili wartość nulla to nazwali tą wartość błędem wartym miliard dolarów. Już wcześniej operator bezpieczeń nawigacji był stosowany w innych językach. Słyszałem, że ma być null-conditional operator, ale myślałem, że ten operator promuje projekt Roslyn, więc nie wiedziałem czy ten operator będzie standardowo w C#.




A poniżej jest filmik z konferencji BUILD 2014:



A tutaj filmik z TechEd


Co prawda, w prezentacjach są te same przykłady kody, jakby nie mogli wymysleć innych zastosowań niż konwerter tablicy pointów do JSONa :)
Wydaje mi się, że w taki sposób próbują utrwalić wiedzę.

Jeszcze podoba mi się operator nameof. Wiem, że parę moich znajomych na ten operator czekało.

środa, 12 listopada 2014

Wyjście na notatnik czyli oczekiwany out-notepad w PS

Bawiąc się z PowerShellem, zauważyłem brak wysłania rezultatów do notatnika w stylu Out-GridView. Postanowiłem napisać skrypcik, który by to umożliwiał. Na samym początku jest potrzebna funkcja do znajdywania okna z aplikacją oraz wysyłania tekstu do niej. Skorzystamy z biblioteki user32.dll. Skrypt wyglada następująco:

$User32MethodDefinition = @'
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);    
'@

$User32 = Add-Type -MemberDefinition $User32MethodDefinition -Name 'user32' -Namespace 'Win32' -PassThru
Za każdym razem jak uruchomimy skrypt to będziemy musieli stworzyć nowy obiekt $user32 (i będziemy musieli wiedzieć czy obiekt ma wymagane metody). Dlatego potrzebna nam będzie funkcja sprawdzająca metody statyczne na obiekcie
function Test-StaticMembers
{
    param(
    [ValidateNotNull()]
    $objectToTest
    , 
    [string[]]
    $members)

    [array]$result = $objectToTest.GetMembers() |
        ? {$_.IsStatic -eq $true -and $_.IsPublic -eq $true} |
        ? {$_.IsAbstract -eq $false -and $_.IsConstructor -eq $false} |
        ? { $members -contains $_.Name}

    if( $result.Count -eq $members.Count)
    { return $true}
    
    throw "Not all member are in object. Searching for members: $members"
}
Sprawdzanie obiektu wygląda następująco:
if( ! $User32 -OR !(Test-StaticMembers -objectToTest  $User32   -members 'FindWindowEx','SendMessage' ) )
{
    $User32 = Add-Type -MemberDefinition $User32MethodDefinition -Name 'user32' -Namespace 'Win32' -PassThru
}
Teraz będziemy mogli stworzyć funkcje do wysyłania outputu tekstu do dowolnej aplikacji:
Function Out-Applciation
{
    Param(
        [Parameter(ValueFromPipeline = $true)] 
        [string]
        $text
        ,
        [switch]
        $toAll,
        [switch]
        $passResult
        ,
        [string]
        $appName
    )
    Begin{
            $textArray = @()
            $intPtr = New-Object 'IntPtr' -ArgumentList '0'

            if($toAll)
            {
                $appProcesses = Get-Process -Name $appName -ErrorAction SilentlyContinue 
            }
            else
            {
                $appProcesses =@( Start-Process -FilePath $appName -PassThru )
            }

            #check if process list exist
            if(! $appProcesses)
            {
                Start-Process -FilePath $appName 
                $appProcesses = Get-Process -Name $appName 
            }
    }
    Process{
        $textArray += $text
    }
    End{
        $textString = $textArray  | out-string
        $appProcesses | %{
           $findWindowExTryCounter = 0
           while(!$windowExChild -OR $windowExChild -eq 0)
           {
            $windowExChild =  $User32::FindWindowEx($_.MainWindowHandle, $intPtr,'Edit', $null )
            
            #if FindWindowEx will not work
            $findWindowExTryCounter++
            if($findWindowExTryCounter -gt 100)
            {
                throw "Can not find window $($_.MainWindowHandle) for $appName"
            }
            else
            {
                Start-Sleep -Milliseconds 100 
            }
           }
           $result = $User32::SendMessage($windowExChild, '0x000C', 0,  $textString )
           
           if($passResult) 
           {$result} #if 1 then is has been send
        }
    }
}
Do tego stworzymy pomocna funkcję do wysyłania wyjścia na notepad:
function Out-Notepad
{
    param(
        [switch]
        $toAll,
        [switch]
        $passResult
    )
    Begin{
        $appName = 'notepad'
    }
    End{
        $input | Out-Applciation -toAll:$toAll -passResult:$passResult -appName $appName
    }
}

Dla przykładu zastosowania out-notepad, pomocna nam będzie funkcja numerująca stringi w kolekcji:
function Numerate-String
{
     Param(
        [Parameter(ValueFromPipeline = $true)] 
        [string]
        $text,

        [string]
        $seperator=':'
        )

    begin{
        $count =0
    }
    process{
            $result = "$count $seperator"+ [System.Environment]::NewLine + $text
            $count++
            return $result
        }
}​

A wywołanie może byglądać następująco:
gc applicationName.log | Numerate-String | Out-Notepad

piątek, 17 października 2014

Bulge bracket, Tier one i Boutiques

Jak zaczynałem prace w banku inwestycyjnym to jeszcze nie znałem tych określeń. Te określenia są tylko dla banków inwestycyjnych. Określenie "Bulge bracket" odnosi się do grupy z największymi bankami inwestycyjnymi. Te bank mogą udzielać największy zakres usług inwestycyjny. Tranzakcie mogą nawet wynosić do 10 miliardów dolarów. Takie banki nazywane są large-cap'ami, a banki z transakcjami między 2 a 10 mld to middle-cap'y. Zazwyczaj bulge bracket'y udzielają takich usługi jak doradztwo, sprzedaż, Market-Making, badanie rynku i instrumentów finansowych takich jak akcje, derywatywy, kredyty itp. Banki te są bardzo często zaangażowane w tworzenie nowych produktów takich jak papiery wartościowe zabezpieczone hipoteką (mortgage-backed securities), swapy ryzyka kredytowego, czy w handel emisją CO2.

Tier one's to pretendenci do bycia w grupie Bulge bracket'ów. Są to duże banki, które udzielają prawie takich samych usług, ale jeszcze nie osiągnęły statusu Bulge bracket.

Boutiques są to małe banki inwestycyjne, których transakcje nie przekraczają mld USD.

Zawsze zapominam jak się nazywają te grupy banków.

czwartek, 2 października 2014

Jednolinijkowce do PATH w PS

Coraz częściej zamiast dużych projektów w PS piszę jednolinijkowce.
Jednym z takich przydatnych jednolinijkowców jest skrypt pokazujący ścieżkę do programu, który znajduje się w PATH:
$env:path.Split(';') | gci -Filter 'app.exe'

Dodatkowo możemy stworzyć funkcje dla wywołania oraz sprawdzanie czy istnieje ścieżka do katalogu:
function Find-EnvPath
{
    param(
    [string]$Filter = "*"
    )
    return $env:path.Split(';') 
          | select -Unique 
          | ? {$_ -and (test-path $_)} 
          | gci -Filter $Filter 
}

Czasami jak uruchamiam sql plusa to nie wiem jaka jest ścieżka do programu, więc uruchamiam jednolinijkowca:
Find-EnvPath "sqlplus.exe" | select FullName​

Wynik dostaje następujący:



Bonus:
Z pomocą może Ci przyjść również komenda where, która już wyświetlania ścieżki do programu
where sqlplus.exe

Jak chcesz wiedzieć, gdzie jest ten program to wpisz:
where where
To tak samo gdybyś wpisał w wyszukiwarce google słowo 'google' :)
Powstało troszkę mitów o tym.


środa, 1 października 2014

Certyfikat z LabView

Zdałem egzamin na certyfikat LabVIEW Associate Developer. Pomyślałem sobie, że pochwale się :) Ale nie to jest intrygujące. Problem w tym, że jest lista osób z certyfikatem CLAD, gdzie publicznie zamieszczony jest numer telefonu do tych osób.

Co ciekawe, w Polsce jest 980 osób z certyfikatem CLAD z czego ponad połowa (ponad 495) osób z Polski jest z obszaru krakowskiego.

niedziela, 31 sierpnia 2014

UnGroup w PS

Ungroup to alias, jakiego mi brakowało w PowerShellu. Bardzo często grupuje obiekty, ale nie ma bezpośredniej możliwości odgrupowania tej kolekcji. Poniższy kod przedstawia tą możliwość:
function ungroup-object 
{
    process {
        $_ | %{$_.Group | %{$_} }
    }
}
Odgrupowanie wygląda jak by brało udział w konkursie na najbardziej nieczytelny kod w PS:)

Stwórzmy alias do funkcji ungroup-object:
New-Alias -Name 'ungroup' -Value 'ungroup-object' -Description 'Ungroup object which where grouped'​

Przykładem takiego odgrupowania może być wyświetlenie wszystkich definicji aliasów, które maja więcej niż jeden alias:
Get-Alias | group definition | ? {$_.Count -gt 1} | ungroup | sort definition | select name, definition | Out-GridView
Okazało się że takich aliasów mam z 53. Poniżej zamieszczona jest część wyników:

Help dla oracla w PS

Co najbardziej mnie denerwuje w oraclu to ilość błędów jakie dostaje ze względu na infrastrukturę lub złożone ustawienia oracla. Natomiast te błędy są całkiem dobrze udokumentowane m.in dzięki portalowi ora-code. Napisałem skrypcik do pobierania informacji o błędach z takiej strony. Skrypt znajduje się w module PS do Oracle, przy którym ostatnio rozwijam.
Skrypt jest podany poniżej:
function Get-OracleHelp
{
    param(
    [ValidateNotNullOrEmpty()]
    [Alias('code')]
    [string]$errorCode
    )

    [ref]$errorNumber=0
    $isNumber = [int]::TryParse($errorCode, $errorNumber)
    if($isNumber)
    {
        $errorCode = 'ORA-{0:00000}' -f $errorNumber.Value
    }

    $errorCode = $errorCode.ToUpper()

    $url = "http://{0}.ora-code.com/" -f $errorCode


    $request = Invoke-WebRequest -Uri $url  -UseDefaultCredentials

    $table = $request.ParsedHtml.body.getElementsByTagName('table')

    $trs = $table | %{$_.getElementsByTagName('tr')} | ? {$_.getAttributeNode('valign').value -eq 'top' }

    $desc=''
    $cause=''
    $action=''

    $trs | %{
        [string]$text = $_.innerText

         if( $text.StartsWith($errorCode, 'CurrentCultureIgnoreCase'))
         {
            $desc = $text.Remove(0, $errorCode.Length+1)
         }
 
         if( $text.StartsWith('Cause', 'CurrentCultureIgnoreCase'))
         {
            $cause = $text.Remove(0, 'Cause'.Length+1)
         }
         
         if( $text.StartsWith('Action', 'CurrentCultureIgnoreCase'))
         {
            $action = $text.Remove(0, 'Action'.Length+1)
         }
    }
    
    return  new-object PSObject `
        | Add-Member -MemberType NoteProperty -PassThru -Name 'Code' -Value $errorCode `
        | Add-Member -MemberType NoteProperty -PassThru -Name 'Description' -Value $desc `
        | Add-Member -MemberType NoteProperty -PassThru -Name 'Cause' -Value $cause `
        | Add-Member -MemberType NoteProperty -PassThru -Name 'Action' -Value $action `
        | Add-Member -MemberType NoteProperty -PassThru -Name 'Url' -Value $url 
}
Wywołanie wygląda następująco:
Get-OracleHelp -errorCode ORA-01243
I dostajemy taki opis błędu:

Oprócz całego kodu błedu to można wpisać tylko numer błędu:
Get-OracleHelp -code 12154

sobota, 30 sierpnia 2014

Dynamiczne parametry funkcji w PS ( dynamicParam )

ValidateSet jest fantastycznym atrybutem do parametrów funkcji. Umożliwia podpowiadanie argumentów oraz nie dopuszcza zmienianie wartości. Ma jednak jedną wadę. dozwolone wartości musza być stałymi przedefiniowanymi. Jeżeli chcielibyśmy zwalidować dane wejściowe, które ilość oraz wartości są zmienne to nie mamy takiej możliwości. Z pomocą przychodzi nam dynamiczny parametr. Całkowicie zmienia składnie funkcji. Przykład wywołania dynamicznego parametru jest poniżej. Przypuśćmy, że mamy hashtable z dozwolonymi nazwami oraz ich wartościami

$dparamColl = @{
param1 = 'test';
param2 = 'abc';
param3 = 'xyz'
}

function function_name
{
    param(
     $param
   )

    dynamicParam {

       $attributes = new-object System.Management.Automation.ParameterAttribute
       $attributes.ParameterSetName = "__AllParameterSets" 
       $attributes.Mandatory = $true
       $attributeCollection =      new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
       $attributeCollection.Add($attributes)
       $_Values  = $dparamColl.Keys
       $ValidateSet =      new-object System.Management.Automation.ValidateSetAttribute($_Values)
       $attributeCollection.Add($ValidateSet)
       $dynParam1 = new-object -Type System.Management.Automation.RuntimeDefinedParameter( "dparam", [string], $attributeCollection)
       $paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
       $paramDictionary.Add("dparam", $dynParam1)
       return $paramDictionary
    }

    begin{}
    process{
      return $dparam
    }
    end{}
}

Można stworzyć osobną funkcję, która będzie zwracała RuntimeDefinedParameterDictionary z dynamicznymi parametrami:
function Get-DynamicParam
{
    param(
    [Parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [array]$paramSet, 
    [Parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string]$paramName)

        $attributes = new-object System.Management.Automation.ParameterAttribute
        $attributes.ParameterSetName = "__AllParameterSets" 
        $attributes.Mandatory = $true
        $attributeCollection =      new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
        $attributeCollection.Add($attributes)
        $_Values  = $paramSet
        $ValidateSet =      new-object System.Management.Automation.ValidateSetAttribute($_Values)
        $attributeCollection.Add($ValidateSet)
        $dynParam1 = new-object -Type System.Management.Automation.RuntimeDefinedParameter( $paramName, [string], $attributeCollection)
        $paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
        $paramDictionary.Add($paramName, $dynParam1)
        return $paramDictionary
}

Dynamiczny parametr pierwszy raz zastosowałem jak pisałem moduł do Oracla. W jednej zmiennej przetrzymuje zapytania sql, a funkcja z parametrem dynamicznym wywołuje to zapytanie sql:
$sqlHelpQueryCollection = @{
DatabaseName = @'
select ora_database_name from dual
'@;

Instance = @'
select * from v$instance
'@;
#and many more
}


function Invoke-OracleSqlHelpQuery
{
    [CmdletBinding()]
    param(
    $conn
    )
    dynamicParam {
        return Get-DynamicParam -paramSet $sqlHelpQueryCollection.Keys -paramName 'sqlHelpQuery'
    }

    begin{}
    process{
    $sql = $sqlHelpQueryCollection[$sqlHelpQuery]
    return Get-OracleDataTable -conn $conn -sql $sql
    }
    end{}
}

Efekt dynamicznego parametru jest przedstawiony poniżej:
Cały kod modułu jest na githubie.


niedziela, 24 sierpnia 2014

PowerShell moduł do Oracle

Zacząłem pisać ps moduł do bazy danych w Oraclu. Kod źródłowy jest na githubie.

Na samym początku należy zainicjalizować moduł.
Load-OracleAssemblyes  'path_to_oracle_dataacces_dir\Oracle.DataAccess.dll'
Będziemy mogli przeglądać hasła oraz przeglądać połączenia w TNS:
$secPass, $pass = Get-Password

Get-TnsOracleConnectionString | Out-GridView
Można pobrać połączenie z app.configu oraz sprawdzić wersję servera:
$connectStr = Get-ConfigConnectionString 'path_to_app_dir\App.config' DatabaseNr1

Get-OracleServerVersion $connectStr


A wywoływanie zapytania SQL jest bardzo proste. Poniżej jest przykład zapisania do csv wszystkich obiektów w bazie danych, które stworzyliśmy dla schematu 'my':
$conn = new-OracleConnection $connectStr
$conn.Open()
$dbObj =Get-OracleDataTable -conn $conn -sql "select * from all_objects where owner like 'my' order by last_ddl_time desc"
$conn.Close()

$dbObj | Export-Csv -Path 'LastChangedObjects.csv'

Oczywiście można wywołać polecenie z pliku, gdzie znajduję się zapytanie sql:
Get-OracleDataTable -file file.sql -conn $conn

Gdy będziemy przeglądać rekordy w bardzo dużej tabeli to będziemy mogli uzyskać wyjątek z brakiem pamięci:

Błąd powyższy odnosi się do braku pamięci:
#out-lineoutput : Exception of type 'System.OutOfMemoryException' was thrown.
# + CategoryInfo : NotSpecified: (:) [out-lineoutput], OutOfMemoryException
# + FullyQualifiedErrorId : System.OutOfMemoryException,Microsoft.PowerShell.Commands.OutLineOutputCommand

Wtedy zamiast ładować wszystko do adaptera to można odczytywać z DataReader'a:
$reader = Get-OracleDataReader  -conn $conn -sql "select * from all_objects"

while($reader.Reader())
{
 # operacja na reader
}

Dodatkową funkcjonalnością jest porównywanie tabel dla różnych baz danych. Poniżej jest przykład sprawdzenia czy na 2 bazach są takie same tabele z takim samym właścicielem (owner) i z taką samą ilością rekordów:
$connStr1 = 'Data Source=...'
$connStr2 = 'Data Source=...'

$conn1 = New-OracleConnection  $connStr1
$conn2 = New-OracleConnection  $connStr2

$allTables1  = Get-OracleSystemTables -conn $conn1 -SystemTable ALL_TABLES
$allTables2  = Get-OracleSystemTables -conn $conn2 -SystemTable ALL_TABLES

Compare-Object 
($allTables1 | select TABLE_NAME,OWNER,NUM_ROWS)  
($allTables2 | select TABLE_NAME,OWNER,NUM_ROWS)  -IncludeEqual 

środa, 13 sierpnia 2014

Wizyta w MOCAKu


Ostatnio byłem w Muzeum Sztuki Współczesnej w Krakowie. Zawsze takie muzea powodują skrajne emocje. Jednym wystawy się nie podobają, inni uwielbiają, a jeszcze inni z powodów politycznych nie chcą chodzić do takich muzeów. W tym muzeum spodobał mi się kolaż z europejskich pocztówek:




A gdzieś na uboczu były takie zdjęcia:

wtorek, 12 sierpnia 2014

Monitorowanie telefonów w UE

Obejrzałem filmik na TEDx o monitorowaniu rozmów telefonicznych. Słyszałem o śledzeniu przez NASA, ale nie byłem świadomy, że rejestrowane są rozmowy telefoniczne oraz przetwarzana jest lokalizacja rozmówcy w EU. Ten cały monitoring jest dzięki dyrektywie 2006/24/WE, która zobowiązała wszystkie państwa członkowskie UE do uchwalenia przepisów nakazującym operatorom telekomunikacyjnym do przechowywania i udostępniania organom ścigania danych swoich klientów. Co ciekawe, wg. Europejskiego Trybunału Sprawiedliwości ta dyrektywa jest niezgodna z konstytucją niektórych krajów oraz narusza prawo do prywatności.





Więcej o tym można poczytać tutaj lub tutaj lub tutaj.
Czy ktoś wie, gdzie można znaleźć dane, jacy europosłowie głosowali za tą dyrektywą??

czwartek, 31 lipca 2014

Pseudorekurencja w Oracle

Spróbowałem moich sił w oracle w wywołaniach rekurencyjnych. Skorzystałem z klauzury WITH. Tak na prawdę nie są to wywołanie rekurencyjne. Poniżej przedstawiam przykład wyświetlania wartości silni:
with 
Factorial (n,fact) as (
select 
0 as n,
1 as fact
from dual

union all

select 
n +1, (n+1)*fact
from Factorial
where n < 20
)
select * from Factorial 
;
A tutaj przykład wyliczenia liczby ruchów z postaci jawnej dla problemu wieży Hanoi:
with 
hanoi  (n,counts) as (
select 
1 as n,
1 as counts
from dual

union all

select 
n +1, POWER(2,n+1) -1
from hanoi 
where n < 20
)
select * from hanoi  
;
Wśród standardowych przykładów rekurencji zawsze musi wystąpić ciąg Fibonacciego. Zapytanie wyświetlające liczbę oraz wartość fibonacciego dla tej liczb, wygląda następująco:
with 
fibonacci (n,fib, fibadd) as (
select 
0 as n,
0 as fib,
1 as fibadd
from dual

union all

select 
n +1, fibadd, (fib + fibadd)
from fibonacci
where n < 100
)

select n,fib from fibonacci 
;

Jak pewnie zauważyłeś, to nie tworzysz modelu rekurencyjnego z warunkami stopu, ale model iteracyjny z warunkami początkowymi.

poniedziałek, 28 lipca 2014

Produktywne makra Visual Studio

Chcę Ci przedstawić 2 makra, które pomagają mi w pisaniu kodu. Pierwsze makro dodaje nawiasy okrągłe do zaznaczonego tekstu w edytorze. Super się sprawdza makro dla kodu w PowerShell czy dla FSharp. Poniższy kod wystarczy zapisać w makrze VS:
Public Module BracketIt
    Public Sub AddBrackets()
        Dim s As Object = DTE.ActiveWindow.Selection()
        If s.Text.StartsWith("(") And s.Text.EndsWith(")") Then
            s.Text = s.Text.Substring(1, s.Text.Length - 2)
        Else
            s.Text = "(" + s.Text + ")"
        End If
    End Sub
End Module
Skrótem klawiszowym do wywołania tego makra mam ustawione na:
Shift+CTRL+A, Shift+CTRL+B,
Równie dobrze można było dodać nawiasy ostrokątne do edycji plików xml.
Drugim makrem jest asercja sprawdzająca czy wartość jest nullem:
Public Module AddAssert
    Public Sub IsNotNull()
        Dim s As Object = DTE.ActiveWindow.Selection()
        Dim assertStr As String = "Assert.IsNotNull"

        If s.Text.Length > 0 Then
            s.Text = assertStr + "(" + s.Text + ")"
        Else
            s.Text = assertStr
        End If
    End Sub
End Module
A skrótem klawiszowym jest:
Shift+CTRL+A, Shift+CTRL+A
Te 2 makra u mnie się sprawdzają. Jeśli masz swój pomysł na makro to podziel się nim.

niedziela, 27 lipca 2014

Count lines of code

CLOC to tool do wyliczenia liczby plików różnych formatów oraz wylicza ile jest tych linijek kodu oraz komentarzy w tych plikach.Jest to tool, bardzo prosty w działaniu. Zacząłem pisać wrapper na PowerShella.
Pierwsza funkcja już jest:
function Get-Loc
{
    param($location="."
)
    $commandLine = "$location --csv"
    $clocResult = & $clocPath $location --csv
    $indexOfLineCsvToParse = [array]::IndexOf($clocResult, "")
    if($indexOfLineCsvToParse -lt 0)
    {
        throw "cant get empty line in cloc result"
    }
    $csvToparse = $clocResult[$indexOfLineCsvToParse..($clocResult.Length)]
    $csvFeed = ConvertFrom-Csv $csvToparse -Delimiter ',' | select  files,language,blank,comment,code  

    return  $csvFeed | %{
        return New-Object PSObject  -prop @{
                                    Files = [int]($_.files);
                                    Language = $_.language;
                                    Blank = [int]($_.blank);
                                    Comment = [int]($_.comment);
                                    Code = [int]($_.code);
                               }
                    } 
}
Aby wyświetlić statystykę projektów to wystarczy wywołać:
 Get-Loc "C:\ProjectDir"
Skrypt jest dostępny w github'a.

sobota, 19 lipca 2014

Reinstalacja pakietów nuget

Pewnie miałeś taką potrzebę reinstalacji pakietu nuget z poziomu Visual Studio. Domyślnie, nuget nie ma możliwości reinstalacji pakietu w jednej komendzie. Poniżej zamieszczam skrypt do wykonania tego zadania:
function Reinstall-Package {
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Id,

        [Parameter(Mandatory = $true)]
        [string]
        $Version,

        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        $ProjectName,

        [switch]
        $Force
    )

    if (-not $ProjectName) {
        $ProjectName = (get-project).ProjectName
    }
    Uninstall-Package -ProjectName $ProjectName -Id $Id -Force:$Force
    Install-Package -ProjectName $ProjectName -Id $Id -Version $Version
}

Przykład wywołania dla reinstalacji danego pakietu dla wszystkich projektów w solucji jest poniżej:
Get-Project -All |
     ?{ $_ | Get-Package | ?{ $_.Id -eq 'bla.bla.bla' } } |
         %{ $_ | Reinstall-Package -Id 'bla.bla.bla' -version 1.0.0 -Force }

Nie będziesz mógł załadować modułów, więc będziesz musiał wywołać skrypt Reinstall-Package z nugetowej konsoli lub możesz dodać tą funkcję do pliku z profilem:
notepad $Profile​

poniedziałek, 14 lipca 2014

Problem ucztujących filozofów w PowerShell

Już wcześniej chciałem przedstawić moje rozwiązanie problemu pięciu ucztujących filozofów, ale jak zwykle ciężko znaleźć chwilkę czasu. Tak na prawdę moje rozwiązanie nie jest ostatecznym rozwiązaniem tego problemu, ale implementacją różnych możliwości uruchomienia symulacji ucztujących filozofów. Aby uruchomić symulację wystarczy uruchomić skrypt:
Run-DPP -showAll

Na ekranie uzyskamy liczby:
Status: 0 0 0 1 0
Meals: 5 2 4 3 3

Górny wiersz określa semafory o wolnych widelcach, a dolny wiersz określa ile razy dany filozof jadł. Z górnego wiersza można odczytać, że widelec przedostatni jest wolny.

Zacząłem rozwiązywać ogólny problem ucztujących n-filozofów. Poniżej przykład statusu dla 10 filozofów:


Symulacja działania filozofów jest na podstawie Jobów. Przy każdym wywołaniu skryptu następuję usunięcie job'ów:
    get-job | Stop-Job
    get-job | Remove-Job 


Na potrzeby rozwiązania problemu, użyto dodatkowego joba, który zarządza wszystkimi filozofami - kelner obsługujący filozofów. Implementacja kelnera jest w pliku Run-Waiter.ps1.
for( $i=0; $i -lt $philosopherNumber ; $i++) 
{
        Start-job -FilePath $scriptPath  -ArgumentList  $i,$scriptLocation -Name "Philo_$i"
} 

Każdy filozof prosi kelnera o widelec (request +1).
Kelner sprawdza listę próśb (request >0) i jak widelce są dostępne, podchodzi do filozofa i pozwala na wzięcie widelca (response +1).
Filozof sprawdza odpowiedz (response >0), bierze widelec i dziękuje za obsługę (request -1).
Kelner odchodzi od filozofa (response -1)
W taki sposób, filozof jest odpowiedzialny za zapis requestów, a kelner odpowiedzialnego za zapis responsów.




Dla kelnera wprowadziłem opcje randomizacji wyboru od którego filozofa za zacząć pytać czy czegoś nie potrzebuje. Częściowo rozwiązanie to rozwiązało problem nadawania priorytetu filozofom parzystym przed innymi filozofami, gdy mamy parzystą liczbę wszystkich filozofów.



W pliku DiningPhilosophersProblemHelper.ps1 są wszystkie funkcje współdzielone między kelnerem, a filozofami. PS nie ma pamięci współdzielonej pomiędzy wszystkie job'y czy procesy. Komunikację między job'ami jest zaimplementowana poprzez zmienne środowiskowe, które są dostępne z poziomu użytkownika:
[Environment]::GetEnvironmentVariable($name, [System.EnvironmentVariableTarget]::User)

Tablica określająca wolne zasoby widelców jest zapisana w zmiennej środowiskowej jako string:
function Set-ArrVariable([string]$name, [array]$arr)
{
    Set-ArrVariableStr $name ($arr  -join ';')
}

Zapis danych do zmiennej środowiskowej na poziomie użytkownika jest naszą sekcją krytyczną. Często można było uzyskać wielokrotne zwiększenie lub zmniejszenie wartości w tablicy:


Synchronizacja zapisu zmiennej środowiskowej jest za pomocą globalnych muteksów dostępnych przez wszystkie job'y. Przykład tworzenia globalnego muteksa jest poniżej:
$UsedForksMutex = new-object  System.Threading.Mutex ($false, 'Global\UsedFork')

Wprowadziłem opcje umierania filozofów, gdy za długo czekają na sztućce (lub na obsługę kelnera).
[bool]$mayStarve = $false
Czas potrzebny dla filozofa na myślenie oraz jedzenie jest ustawiony jako zmienna losowa. Domyślnie jest to wartość jednostkowa.

W planach mam jeszcze dodatnie
- ograniczenia do parzystej liczby filozofów, którzy mogą ubiegać się o sztućce
- pozwolenie filozofom wzięcia widelców, tylko jeżeli 2 z nich są dostępne ( jak na razie filozof bierze jeden widelec, a później próbuje wziąć drugi)
- filozofowie z parzystymi numerami najpierw wybierają widelce po prawej stronie, a filozofowie z numerami nieparzystymi wybierają najpierw widelce po lewej stronie
- rozwiązanie probabilistyczne (losowe wybieranie strony pierwszego widelca, a później znów losowanie prawdopodobieństwa wzięcia drugiego widelca, w przypadku nie wzięcia tego drugiego widelca opuszcza się widelec pierwszy)
- ustalenie priorytetów wyboru widelców (przy podnoszeniu najpierw wybiera się sztućce o numerach niższych, a przy oddawaniu najpierw oddaje się sztućce o numerach wyższych)
- rozwiązanie czystych i brudnych widelców
- ustawienie pierwszeństwa w jedzeniu dla filozofa o najmniejszej ilości skonsumowanych posiłków


Co prawda implementacja problemu 5 filozofów nie jest jeszcze gotowa, ale mogę wysunąć wnioski, że dużo przyjemniej rozwiązuje się ten problem w innych językach programowania - przynajmniej nie trzeba pracować na zmiennych globalnych czy na job'ach. Dużo przyjemniejsza była by implementacją tego problemu w C# niż w PS. PS jak na razie nie ma prostych sposobów synchronizacji job'ów. Taka prosta sprawa jak wyświetlanie komunikatów też nie była prosta. Write-Host nie wyświetla komunikatów w konsoli dopóki nie pobierze się job'a. Można było zawiesić komputer za pomocą symulację dla 20 filozofów i przetrzymując wszystkie komunikaty w pamięci. Nawet jak wyjdziemy z debuggera to job'y i tak będą wykonywać się.

Cały kod źródłowy można pobrać z github.

wtorek, 8 lipca 2014

Doping dla PowerShell ISE

Przeglądając polski blog o PowerShellu, natrafiłem na bardzo ciekawy wpis o dodatku do PowerShell ISE - ISESteroids. Od razu musiałem sprawdzić ten dodatek i muszę powiedzieć, że jestem bardzo miło zaskoczony. Tool można pobrać z tego linka, a tutaj jest art. o nim w PowerShell Magazine.

Bardzo polecam :)

niedziela, 6 lipca 2014

Sprawdzanie plików z datą

Jeżeli pracowałeś z źródłem danych w formie plików tworzonych każdego dnia, to za pewne miałeś problem ze sprawdzeniem czy są wszystkie pliki. Przesyłam proste skrypty sprawdzające takie pliki.
Na początku potrzebujemy funkcję do iteracji po dacie:
function Iterate-Date
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True,Position=1)]
        [ValidateNotNull()]
        [DateTime]
        $startDate
        , 
        [Parameter(Mandatory=$True,Position=2)]
        [ValidateNotNull()]
        [DateTime]
        $endDate
    )

    for($i = $startDate; $i -le $endDate; $i = $i.AddDays(1))
    {
        $i
    }
}
Potrzebna będzie funkcja sprawdzająca czy istnieje nazwa pliku z podanym patternem:
function Test-FileName    
{
    param(
        [string]$path='.'
    )

    process{
        $filter= $_
        $items = gci -Path $path -Filter $filter 

        new-object PSObject | Add-Member -MemberType NoteProperty -Name Filter -Value $filter -PassThru | 
        Add-Member -MemberType NoteProperty -Name Items -Value $items -PassThru |
        Add-Member -MemberType NoteProperty -Name Contains -Value ([bool]$items) -PassThru
    }
}
Samo sprawdzanie czy istnieje plik z patternem yyyy-MM-dd* jest poniżej:
Iterate-Date -startDate ([DateTime]("2005-02-13")) ([DateTime]("2014-06-16")) | 
%{ $_.ToString('yyyy-MM-dd') + "*"} | group | %{$_.name} |
Test-FileName |
? {-not $_.Contains}
Aby uprościć powyższe wywołanie to możemy zamienić group za pomocą poniższej funkcji:
function Get-KeyGroupObject
{
    param(
        [string]
        $keyPropName = 'name'
    )
    #in variable  $input (enumerator type) we have all items
    $Input | group | %{$_.$keyPropName​} 
}
A sprawdzanie wygląda następująco:
Iterate-Date -startDate ([DateTime]("2005-02-13")) ([DateTime]("2014-06-16")) | 
%{ $_.ToString('yyyy') + "*"} | Get-KeyGroupObject |
Test-FileName |
? {-not $_.Contains}

piątek, 4 lipca 2014

Algorytmy geometryczne w PowerShell

Wystartowałem z kolejnym projektem w PS, tym razem z algorytmami geometrycznymi. Chciałbym zrobić framework oraz zaimplementować parę alg. geometrycznich. Dopiero co zacząłem implementację i dużo jeszcze jest przede mną.
Na samym początku można stworzyć punkt dla int jak i dla float:
new-p 1 2
new-pf 1.4 2.1

Aby funkcje zadziałały to należy uruchomić funkcje Init-GeoAlg znajdująca się w pliku PointsCommon.ps1.
Możesz wygenerować 100 punktów od punktu (0,0) do (100,100) wywołując:
Generate-PF -iterateNum 100

Lub za pomocą wyrażenia lambda (oficialnie nie ma w PS :) ) można podać scriptblock zwracający wartość równej sumy poprzedniej wartości i iteratora:
Generate-PF -iterateNum 100 -funcX {param($prevVal, $i) $i + $prevVal}

Jak na razie to nie wiele alg. zaimplementowałem (spr. czy punkt znajduje się na odcinku, sortowanie punktów po koncie nachylenia względem osi OX, znajdywanie parę punktów o najmnijszej odległości od siebie itd.).

Z pomocą pestera, zacząłęm pisać testy w stylu BDD, ale jak na razie nie wiem jak można weryfikować mocka na alias w pipelinie w wywołaniu rekurencyjnym. Tej funkcjonalności brakuje mi w pester.


Projekt można znaleźć na moim github. Pomysł na projekt o alg. geometrycznych powstał po prezentacji o tym temacie:


czwartek, 3 lipca 2014

Reaktywacja c++

Od wielu lat nie siedzę w c++, ale ostatnio obejrzałem filmik co ma do zaoferowania nowy C++14 i C++11. Jestem w głębokim szoku. Nowe featury pochodzą od takich popularnych języków jak java, c# czy python. Dawno temu uczyłem się c++, ale kiedy tylko natrafiłem na przejrzysty język c#, to od razu c++ poszedł w niepamięć. Myśłe, że dużo jest osób, którzy tak samo zrobili jak ja. Czy zacznie rosnąć popularność c++ ? Nie wiem.
Poniżej filmik o c++:




środa, 2 lipca 2014

Sprawdzenie wolnego obszaru na dysku


Tak często potrzebuje sprawdzać wolny obszar na dysku, że postanowiłem zamieścić ten krótki kod:
gwmi win32_logicaldisk | select DeviceID, @{n="FreeSpace (GB)"; e={$_.FreeSpace/1GB}},  @{n="Size (GB)"; e={$_.Size/1GB}}

Oprócz tej komendy chciałbym Ci przedstawić tool o nazwie WinDirStat. Oczywiście można pobrać ten tool z chocolatey.

Testy w PowerShell

Ostatnio zająłem się testowaniem skryptów PowerShell. Skorzystałem z narzędzia pester. Bardzo spodobał mi się ten tool.
Dla przykładu mamy funkcję do testowania, która dodaje metody do kastowania na int, double oraz byte
function Add-CastingFunctions($value) {
    return Add-Member -InputObject $value -Name ToInt `
           -MemberType ScriptMethod -PassThru -Force -Value `
           { [int] $this } |
           Add-Member -Name ToDouble `
           -MemberType ScriptMethod -PassThru -Force -Value `
           { [double] $this } |
           Add-Member -Name ToByte `
           -MemberType ScriptMethod -PassThru -Force -Value `
           { [byte] $this } 
}
Poniżej mamy test ( w osobnym pliku):
Describe "when added cast function to string '5'"  {
    $patched = Add-CastingFunctions("5")

    Context "and I do not attempt to cast it" {
        It "returns a string" {
            $patched.GetType().Name| Should be "string"
        }
    }

    Context "and I attempt to cast it as an integer" {
        It "returns a value that is of type integer" {
            $patched.ToInt().GetType().Name| Should be ("Int32")
        } 
    }

    Context "and I attempt to cast it as a double" {
        It "returns a value that is a double" {
            $patched.ToDouble().GetType().Name| Should be ("Double")
        }
    }

    Context "and I attempt to cast it as a byte" {
        It "returns a value that is a byte" {
            $patched.ToByte().GetType().Name| Should be ("Byte")
        }
    }
}

W tym samym pliku co jest test, musimy dodać nagłówek:
$pwd = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests", "")
. "$pwd\$sut"
Niestety, trzeba w każdym pliku dodawać ten sam nagłówek, więc wpadłem na pomysł, aby dodać ten nagłówek do snippeta.
New-IseSnippet -Title 'pester-header' -Description  'Adds pester header' -Text '
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
. "$here\$sut" 
' -Force
Teraz, dzięki temu snippetowi możesz w PS_ISE dodać nagłówek za pomocą CTRL + J

sobota, 17 maja 2014

Rozwiązanie wieży Hanoi za pomocą listy cyklicznej

Już parę razy pisałem o problemie wieży Hanoi oraz rozwiązaniu jej.

Tym razem chciałbym przedstawić Ci rozwiązanie iteracyjne wieży Hanoi za pomocą listy cyklicznej w PowerShellu.
Algorytm jest bardzo prosty:
1. Połącz słupki w sposób cykliczny
2. Jeżeli liczba wszystkich krążków jest parzysta to ruch słupków jest zgodny z ruchem zegara. W przeciwnym przypadku jest to ruch przeciwny do ruchu zegara.
3. Znajdź najmniejszy krążek i przenieś go na następny słupek
4. Jeżeli wszystkie krążki jest są na słupku docelowym to zakończ program
5. Dla każdego krążka, który nie jest najmniejszym krążkiem spróbuj przenieś krążek na następne miejsce.
6. Przej do kroku nr 3


Do implementacji tego algorytmu będzie nam potrzebny typ stosu. Skorzystamy z akceleratora. =
$accelerators = [psobject].Assembly.gettype("System.Management.Automation.TypeAccelerators")
$stackType = [type]'System.Collections.Generic.Stack`1'
$accelerators::Add('stack', $stackType)
Stworzymy węzeł, który w problemie wieży Hanoi będzie jako słup:
function Create-Node
{
    param($value, $previous, $next, $key=$([guid]::NewGuid()))

    return new-object PSObject | 
               Add-Member -MemberType NoteProperty -Name "Key" -Value  $key -PassThru |                
               Add-Member -MemberType NoteProperty -Name "Value" -Value $value  -PassThru |
               Add-Member -MemberType NoteProperty  -Name "Next" -Value $next  -PassThru |
               Add-Member -MemberType NoteProperty  -Name "Previous" -Value $previous -PassThru
}
I teraz będziemy mogli stworzyć listę cykliczną z tablicy:
function Create-CircularList
{
    param([array]$arr, [switch]$nonCircular, [array]$keys)

    $CircularList =@()
    for($i=0; $i -lt $arr.Length; $i++)
    {
        $node = Create-Node -value $arr[$i] -key $keys[$i] 
        if($i -gt 0)
        {
            $prevNode =   $CircularList[$i-1]
            $node.Previous = $prevNode
            $prevNode.Next = $node
        }
        $CircularList += $node
    }

    if(!$nonCircular)
    {
        $firstNode = $CircularList[0]
        $lastNode = $CircularList[$arr.Lengt-1]

        $firstNode.Previous = $lastNode 
        $lastNode.Next = $firstNode
    }
    return $CircularList
}
Jeszcze będziemy potrzebować funkcji która by zwracała index listy, która posiada najmniejszy krążek:
function Find-ItemInValue
{
    param([array]$arr, [int]$item)

    for($i=0; $i -lt $arr.Length; $i++)
    {
        $value = $arr[$i].Value
        if($value -is [int] -and [int]$value -eq [int]$item)
        {
            return $i
        }
        elseif($value)
        {
            $index = [array]::IndexOf($value,$item)
            if($index -ge 0)
            {
                return $i
            }
        }
    }
    return -1
}
I teraz wystarczy zaimplementować algorytm rozwiązujący wieżę Hanoi:
function Solve-Hanoi
{
[CmdletBinding()]
param(
[Alias("circularList")]$list,
[switch]$optDir
)

$startPeg = $list[0]
$maxItems =  $list.Value.Count
$listMeasure = $list.Value | measure -Minimum -Maximum
$minSpotValue = $listMeasure.Minimum
$maxSpotValue = $listMeasure.Maximum
$endPeg = $list[$list.Length-1]
 
$moveDir = "Next"
if($optDir)
{
    [bool]$even = $maxItems % 2 -eq 0
    if(!$even)
    {
        $moveDir = "Previous"
    }
}

$step=0
 while($endPeg.Value.Count -lt $maxItems)
 {
    for($searchSpot = $minSpotValue; $searchSpot -le $maxSpotValue; $searchSpot++)
    {
        $indexOfMin = Find-ItemInValue -arr $list -item $searchSpot
        if($indexOfMin  -eq -1)
        {
            continue
        }
        $currentPeg = $list[$indexOfMin]
 
        $nextPeg = $currentPeg.$moveDir
        $currentSpot = $currentPeg.Value.Peek()
        if($currentSpot -eq $searchSpot)
        {
            while($nextPeg -ne $currentPeg)
            {
                if($nextPeg.Value.Count -eq 0 -OR $nextPeg.Value.Peek() -gt $currentSpot)
                {
                    $nextPeg.Value.Push($currentPeg.Value.Pop())
                        
                    $step++
                    Write-Verbose "Step: $step"
                    Write-Verbose " $($list | select Key,Value |out-string)"
                    break
                }
                $nextPeg = $nextPeg.$moveDir
            }
        }
    }
}
return $list
}
Wywołanie tego algorytmu jest następujące:
[stack[int]]$pegA = 5,4,3,2,1 #last in, first out
[stack[int]]$pegB = @()
[stack[int]]$pegC = @()

$CircularList= Create-CircularList -arr $pegA,$pegB,$pegC -keys 'A','B','C'
Solve-Hanoi -circularList $CircularList -Verbose -optDir
Możemy wywołać wieżę Hanoi z dowolnego układu:
$arr = 1..4
[array]::Reverse($arr)
[stack[int]]$pegA =$arr
[stack[int]]$pegB = @(6)
[stack[int]]$pegC = @(5)

$CircularList= Create-CircularList -arr $pegA,$pegB,$pegC -keys 'A','B','C'
Solve-Hanoi -circularList $CircularList -Verbose -optDir
No, prawię z dowolnego. Dla takiego układu początkowego musiałem wyłączyć parametr optymalizujący kierunek przesunięcia krążków.
$arr = 1..2
[array]::Reverse($arr)
[stack[int]]$pegA =$arr
[stack[int]]$pegB = 7,6
[stack[int]]$pegC = @(5)

$CircularList= Create-CircularList -arr $pegA,$pegB,$pegC -keys 'A','B','C'
Solve-Hanoi -circularList $CircularList -Verbose 

piątek, 16 maja 2014

Odwracanie hashtable w PS

Postanowiłem napisać odwracanie hashtable. Ta operacja jest nazywana pivotem. Dlaczego to zrobiłem? Przedstawię przykład. Jak stworzysz kolekcję hashtablicy to w PS źle (moim zdaniem) wyświetla się:
$hashtables = @()
$hashtables += @{FName="Arek";  Number=18}
$hashtables += @{FName="Gosia"; Number=1}
$hashtables += @{FName="Dawid"; Number=9}
Wynik tych kolekcji hashowania jest następujący:
Name                    Value                                                                                        
----                    ----                                                                                    
Number                  18                                                                                           
FName                   Arek                                                                                         
Number                  1                                                                                            
FName                   Gosia                                                                                        
Number                  9                                                                                            
FName                   Dawid  

Potrzebna będzie nam funkcja odwracanie hastable:
function Pivot-Hashtable
{
    param([hashtable[]]$hashes)

    $names = ($hashes.keys | group).name

    $hashes | % {
        $hash = $_
        $row = New-Object PSObject
        foreach($name in  $names)
        {
             $row | Add-Member  -MemberType NoteProperty -Name $name -Value $hash[$name]
        }
        return $row
    } 
}
Rezultat jest poniżej:
Pivot-Hashtable $hashtables


Number FName                                                         
------ ----                                                         
18     Arek                                                         
1      Gosia                                                        
9      Dawid  

środa, 14 maja 2014

Mapowanie w Spring.NET za pomocą PS

Spring.NET jest dla mnie bardzo ciężkim frameworkiem i do tego napisałem pomocną funkcję do wyświetlania wszystkich mappingów object:
function Get-SpringMapping
{
    param([string[]]$files)

    $files | %{
        $file = $_
        $springMapping = [xml](gc $file)
        $springMapping.objects.object | %{
            $obj  =$_
            if($obj.name -and $obj.type)
            {
                if($obj.name -ne 'object')
                {
                    [int]$constructors=0
                    if($obj."constructor-arg")
                    {
                        $constructors = $obj."constructor-arg".Count
                    }

                    @{      Name = $obj.name; 
                            Type = $obj.type;
                            ConstructorArgs =  $constructors;
                            File = $file;
                    }
                }
            }
        }
    }
}
Wywołanie wygląda następująco:
$springFiles = gci ".\SpringRep\" -Filter "*.spring" -Recurse 
$mapping = Get-SpringMapping ($springFiles.FullName)
W obiekcie $mapping jest wszystkie mapowania z plików springowych z folderu SpringRep

wtorek, 13 maja 2014

Lista cykliczna w PS

Postanowiłem zrobić listę cykliczną w PowerShellu.
Na samym początku zrobiłem węzeł reprezentujący wartość oraz wskazuje na następny i poprzedni węzeł:

function Create-Node
{
    param($value, $previous, $next)

    return new-object PSObject | 
               Add-Member -MemberType NoteProperty -Name "Value" -Value $value  -PassThru |
               Add-Member -MemberType NoteProperty  -Name "Next" -Value $next  -PassThru |
               Add-Member -MemberType NoteProperty  -Name "Previous" -Value $previous -PassThru
}
Jeszcze do tego jest potrzebna funkcja do wypełnienia tych węzłów:
function Create-CircularList
{
    param([array]$arr, [switch]$nonCircular)

    $CircularList =@()

    for($i=0; $i -lt $arr.Length; $i++)
    {
        $node = Create-Node -value $arr[$i] 
        if($i -gt 0)
        {
            $prevNode =   $CircularList[$i-1]
            $node.Previous = $prevNode
            $prevNode.Next = $node
        }
        $CircularList += $node
    }

    if(!$nonCircular)
    {
        $firstNode = $CircularList[0]
        $lastNode = $CircularList[$arr.Lengt-1]

        $firstNode.Previous = $lastNode 
        $lastNode.Next = $firstNode
    }
    return $CircularList
}
Wywołanie jest bardzo proste:
$list= Create-CircularList -arr "A","B","C"
$item = $list[0]
$item.Next.Next.Next
Zmienna $item reprezentuje pierwszy element, a jak wywołały 3 razy następny element to również uzyskamy pierwszy element z listy.
Jeżeli będziesz chciał stworzyć połączenia, bez cyklu to wystarczy wywołać funkcję z parametrem nonCircular:
$list= Create-CircularList -arr "A","B","C" -nonCircular
$item = $list[0]
$item.Next.Next.Next

poniedziałek, 12 maja 2014

Plik z liczbami w PS

Przesyłam Ci funkcję do tworzenia pliku z liczbami naturalnymi.
function New-RandomNumericFile([uint64]$maxNumer,[uint64]$amount, [string]$fileName)
{
    [uint64]$counter=0
    $stream = [System.IO.StreamWriter]$fileName
    if($stream)
    {
        $random = New-Object 'System.Random'
       
        while($counter -le $amount)
        {
            $counter++
            $randomNumber = $random.Next($maxNumer)
            $stream.WriteLine($randomNumber)
        }
        $stream.Close()
    }
    return  $counter
}
Przydatna będzie funkcja sprawdzająca czy liczby w pliku są posortowane:
function IsSorted-NumericFile
{
    [CmdletBinding()]
    param([string]$fileName)

    $stream = [System.IO.StreamReader]$fileName
    [uint64]$counter=0
    [int]$lastNumber=-1
    [bool]$isSorted=$true
    while(-not($stream.EndOfStream))
    { 
        $line = $stream.ReadLine()
        $counter++
        $number = [Convert]::ToInt32( $line)
        Write-verbose "$counter line has: $number "

        if($isSorted)
        {
               if($lastNumber -gt $number)
               {
                $isSorted =$false
                break
               }
         }
        $lastNumber = $number
    }
    $stream.Close()

    return $isSorted
}
A jak już masz posortowane pliki to można je porównać:
function CompareLine-NumericFile
{
    [CmdletBinding()]
    param([string]$fileLeft,  [string]$fileRight='' )

    [uint64]$counter=0
    $streamLeft = [System.IO.StreamReader]$fileLeft
    $streamRight = [System.IO.StreamReader]$fileRight
    $aresame = $true

    if($streamLeft -AND $streamRight)
    {
        while(-not($streamLeft.EndOfStream))
        { 
            if($streamRight.EndOfStream)
            {
                Write-Verbose "EOF has been reached for file $fileRight"
                $aresame =$false
            }

            $lineLeft = $streamLeft.ReadLine()
            $lineRight = $streamRight.ReadLine()

            if([string]::IsNullOrEmpty($lineLeft) -and [string]::IsNullOrEmpty($lineRight))
            {
                continue
            } 

            if($lineLeft -ne $lineRight)
            {
                $aresame = $false
                break
            }
                
            $counter++
        }
        if($streamLeft.EndOfStream -and -not($streamRight.EndOfStream))
        {
            Write-Verbose "EOF has been reached for file $fileLeft"
            $aresame =$false
        }


        $streamLeft.Close()
        $streamRight.Close()
    }
    else
    {
        Write-Verbose "One of file is empty"
        $aresame  = $false;
    }

    if($aresame)
    {
        Write-Verbose "Checked $counter lines in 2 files"
    }
    else
    {
        Write-Verbose "First inequality in line $counter"
    }

    return $aresame 
}
Wywołanie funkcji do porównywania wygląda następująco:
CompareLine-NumericFile -fileLeft "test_sorted1.txt" -fileRight "test_sorted2.txt" -Verbose

poniedziałek, 5 maja 2014

Ceiling i Floor w PS

Postanowiłem poćwiczyć implementację sufitu i podłogi w PS. Jak jesteś zainteresowany jak działa Ceiling i Floor to sprawdź klasę Math.
Implementacja sufitu jest następująca:
function Get-Ceiling($x)
{
    [int]$ceil = [int]$x
    if($ceil  -ne $x)
    {
        $diff = $ceil - $x
        if($diff -ge 0)
        {
            return $ceil
        }else{
            return $ceil+1
        }
    }
    return $ceil
}
A podłogi jest taka:
function Get-Floor($x)
{
    [int]$floor = [int]$x
    if($floor  -ne $x)
    {
        $diff = $floor - $x
        if($diff -ge 0)
        {
            return $floor-1
        }else{
            return $floor
        }
    }
    return $floor
}
Implementacja algorytmu podłogi i i sufitu jest zupełnie inna niż dla c podobnych języków programowania (C,C++,java, c#) jak i dla VB

Postanowiłem zaimplementować inne funkcje, za pomocą już zdefiniowanych funkcji:

function Get-Sign([int]$x)
{
    if($x -gt 0)
    {
        return 1
    }
    elseif($x -lt 0)
    {
        return -1
    }
    return $x
}

function Get-Abs([decimal]$x)
{
    return  $x * (Get-Sign($x))
}

function Get-Round($x)
{
    if([int]$x -ge 0)
    {
        return Get-Floor($x + 0.5D)   # D for Decimal
    }
    return Get-Ceiling($x - 0.5D)
}

function Get-Sawtooth($x)
{
    return $x - (Get-Floor($x))
}
Funkcja Sawtooth zwraca ułamek ( może powinna się nazywać funkcją zębatej piły)

function Get-Truncate($x)
{
    return (Get-Sign($x)) * (Get-Floor(Get-Abs($x)))
}
Dla funkcji Get-Truncate złe będzie zwracana liczba jeżeli funkcja zwracająca sygnału będzie bez nawiasów:
Get-Sign($x) * (Get-Floor(Get-Abs($x)))


Dodatkowo mamy implementację tych części funkcji dla licz używanych jako wartość pieniądza (z dokładności liczby do 2 miejsc po przecinku):
function Get-RoundMoney($x)
{
    return (Get-Round($x*100))/100    
}

function Get-SawtoothMoney($x)
{
    return [decimal]($x*100) - (Get-Floor($x*100))
}

function Get-TruncateMoney($x)
{
    return (Get-Sign($x)) * ((Get-Floor(Get-Abs([decimal]$x*100)))/100)
}

Dla funkcji Get-TruncateMoney też będziemy mieli problem z ze zmienną $x.
Bez [decimal] nie zadziała Get-TruncateMoney.
Przy wywołaniu Get-TruncateMoney -19.0190 uzyskamy błąd z tekstem:-19.0190-19.0190-19.0190-19.0190-19.0190-19.0190... itd.

Tak się zastanawiam czy nie lepiej było by zaimplementować te funkcje w .NET i skorzystać z wygenerowanej biblioteki.

niedziela, 4 maja 2014

Pobranie ról użytkownika w PS

Kiedy pracujesz zdalnie przez konsole PS i nie możesz uruchomić polecenia PS to być może nie masz uprawnień. Aby sprawdzić z jakimi uprawnieniami pracujesz na konsoli wystarczy uruchomić poleceni Get-Role. Implementacja tej funkcji jest poniżej:
function Get-Role
{
param(
$user =  
$([Security.Principal.WindowsIdentity]::GetCurrent()),

[Security.Principal.WindowsBuiltinRole[]]$role= 
$([Security.Principal.WindowsBuiltinRole].GetEnumValues())
)
 $userPrincipal = New-Object Security.Principal.WindowsPrincipal $user
 $role | %{
    return New-Object PSOBJECT  | 
Add-Member -MemberType NoteProperty 
-Name "Role" -Value $_  
-PassThru |
               
Add-Member -MemberType NoteProperty 
-Name "IsIn" -Value ($userPrincipal.IsInRole($_)) 
-PassThru
 }
}

Jak widać użytkownik nie ma praw administratora.
Gdy tą samą funkcję uruchomisz na konsoli z prawami admina to twój użytkownik też jest w roli administratora.

środa, 30 kwietnia 2014

Restartowanie PowerShell Remoting

Kiedy korzystasz z PSRemoting to pewnie zauważyłeś, że komenda Disable-PSRemoting tak na prawdę nie gasi serwisu i nie usuwa listenerów. Niestety musimy to wykonywać ręcznie. Co prawda przydał by się parametr w poleceniu Disable-PSRemoting.

Poniższy kod wyłącza PSRemoting, serwis i usuwa listenerów:
function Restart-PSRemoting
{
        param(
        [Switch]$restartService,
        [Switch]$removeListeners,
        [Switch]$WhatIf,
        [Switch]$enable,
        [int[]]$listenerPorts=(5985,5986)
        )
        Disable-PSRemoting -Force -WhatIf:$WhatIf

        if($restartService)
        {
            Set-Service winrm -StartupType Manual -WhatIf:$WhatIf
            Stop-Service winrm -WhatIf:$WhatIf
        }

        if($removeListeners)
        {
            gci WSMan:\localhost\Listener -Recurse  | 
            %{ $_.PSPath} | 
            ?{ 
               $port = (gci "$_\port").Value 
               $listenerPorts -contains $port
            }|
            Remove-Item -WhatIf:$WhatIf
        }
        if($enable)
        {
            Enable-PSRemoting -Force -WhatIf:$WhatIf
        }
}

Aby sprawić co zrobi polecenie wystarczy wywołać polecenie z parametrem WhatIF
Restart-PSRemoting -WhatIf -removeListeners -enable -restartService

niedziela, 13 kwietnia 2014

Pamiętaj o aktualizacji pomocy w PS

Dobrą praktyką przy pracy z PowerShellu jest aktualizacja pomocy. Niestety nie zawsze o tym pamiętam :) i dobrze jest dodanie sobie zdarzenia które będzie aktualizowało zadanie. Na samym początku tworzymy trigger, który będzie uruchamiany w piątki wieczorem jak komputer nie będziesz pracował przy komputerze oraz update będzie uruchamiany nawet jak notebook będzie działał na baterie (StartIfOnBattery).
$trigger = New-JobTrigger -Weekly -At "8:00 PM" -DaysOfWeek Friday 
$scheduledOption = New-ScheduledJobOption –StartIfIdle -StartIfOnBattery -RequireNetwork 
Skrypt, który wykonuje update wygląda następująco:
$updateScript = {Update-Help -Module * -Force}
Aby zarejestrować zdarzenie wystarczy wykonać poniższe polecenie
Register-ScheduledJob -Name UpdatePowerShellHelp -Trigger $trigger -ScheduledJobOption $scheduledOption -ScriptBlock $updateScript 
Aby zobaczyć wynik należy przejść do okna z harmonogramem zadań:
start "Taskschd.msc"


Krótko o innowacji w Indiach

Mam takie wrażenie jakbyśmy my (rynek polski, polscy inżynierowie, matematycy na polskiej śmietanie chowani) rywalizowali ze specjalistami z Indii czy z Chin.



Nie jesteśmy lepsi ani gorsi. Każda osoba jest indywidualna. Każda osoba ma swoje umiejętności i zestaw cech.

DNS z cache

Przdstawiam Ci skrypt do pobierania pamięci podręcznej programu rozpoznawania nazw DNS. Dlaczego jest to ważne - spójrz na wpis niebezpiecznika :)

Poniżej jest skrypt, który zwraca Ci wszytkie zapisane w buforze nazwy DNS:
function Get-IPConfigDNS
{
Write-Host "Getting DNS cache info"
$recordNames = ipconfig /displaydns | select-string "Record " #white space on end
    for ($i=0;$i -lt $recordNames.count;$i+=3) 
    {

        $hash= [ordered]@{
          Name=$recordNames[$i].toString().Split(":")[1].Trim()
          Type=($recordNames[$i+1].toString().Split(":")[1].Trim()) -as [int]
          Value=$recordNames[$i+2].toString().Split(":")[1].Trim()
        }
        New-Object -TypeName PSobject -Property $hash
    }
}
Aby uzyskać wszystkie nazwy po prostu wykonaj:
Get-IPConfigDNS | sort name
I pamiętaj, zawsze czyść bufor programu rozpoznawania nazw DNS:
ipconfig /flushdns 

piątek, 11 kwietnia 2014

Dodawanie enuma do PS

Dodawanie enuma do PS nigdy nie było takie proste :)
Skorzystamy z kodu w c#, aby stworzyć taki typ i dodamy do PS:
function Add-Enum
{
    param(
    [ValidateNotNullOrEmpty()]
    [string]$name='Enum1',

    [ValidateNotNullOrEmpty()]
    [array]$members
    )

    $enumJoinMembers = $members -join ', '
    $csharpEnum =   "public enum $name{
        $enumJoinMembers
}"
    Add-Type -TypeDefinition $csharpEnum -Language CSharp
}
Dajmy na to, że chcemy dodać enum, który będzie reprezentował osoby w liczbie pojedynczej. Dodawanie enuma wygląda w następujący sposób:
Add-Enum -name "osoby" -members "ja","ty","on","ona","ono"

I wtedy mamy enum do korzystania:
[osoby]::ja
[osoby]::ty

środa, 2 kwietnia 2014

Konferencja o porażkach

Parę miesięcy temu byłem na pierwszej konferencji poświęconej porażkom. Imprezę uważam za udaną. Konferencja bardziej była nastawiona na porażki w biznesie niż na porażki w innych sferach (życie prywatne, kariera, nauka i tym podobne).


Bardzo spodobało mi się żywe wystąpienie Fabiana:



Jeszcze z Krakowa był Pan Lech, o którym było głośno po filmie "ukłau zamkniętym":


wtorek, 1 kwietnia 2014

poniedziałek, 31 marca 2014

Konwersja bool w PS

Dla mnie konwersja obiektu do typu bool w PS jest ciężka. Wystarczy przejrzeć poniższe przykłady, żeby się przekonać, że zupełnie inaczej się rzutuje do typu boolenowskiego niż w innych językach programowania. Dla uproszczenia dodałem rezultaty poleceń w komentarzach:
[bool]0 #false
[bool]-1 #true

[bool]''  #false
[bool]' ' #true

[bool]"" #false
[bool]" " #true

[bool]"0" #true
[bool]"-1" #true
[bool]"1" #true

[bool]"$true" #true
[bool]"$false" #true

[bool]$false #false
[bool]$null #false
Albo taki o to przykład:
$persistent = “False”
[boolean]$persistent #true
Jak się zastosuje inne metody to wszystko jest dobrze:
[boolean][System.Convert]::ToBoolean( $persistent) #false
[bool]::Parse($persistent) #false
Oprócz logiki dwuwartościowej może istnieć logika trójwartościowa (niewiadoma). Do takiej konwersji już ciężko jest zastosować typ bool.

Dlatego też stworzyłem funkcję do konwersji string na bool:
$TrueValues = @("yes", "y", "1", "tak")
$FalseValues = @("no", "n", "0", "nie")

function ConvertTo-Bool
{
    param(
    [Parameter(Mandatory=$true)]
    $value,
    [switch]$nullable
    )

    [bool]$outBool = $false
    if (![bool]::TryParse($value, [ref] $outBool)) #[ref] for ref and out parameter
    {
        [bool] $containsInTrue = $TrueValues -contains $value;
        if ($nullable)
        {
            if ($containsInTrue)
            {
                return $true;
            }

            if ($FalseValues -contains $value)
            {
                return $false;
            }
            return $null;
        }
        return $containsInTrue;
    }
    return $outBool
}
A poniżej przykłady użycia funkcji do konwersji:
ConvertTo-Bool "false" #$false
ConvertTo-Bool "falsssse" #$false
ConvertTo-Bool "true" #$true

ConvertTo-Bool "false" -nullable #$false
$shoud_be_null = ConvertTo-Bool "falsssse" -nullable #$null

ConvertTo-Bool "true"-nullable #$true

ConvertTo-Bool "nie" #$false
ConvertTo-Bool "tak" #$true

ConvertTo-Bool "nie" -nullable #$false
ConvertTo-Bool "tak" -nullable #$true


Algorytm wydania reszty

Znów bawiłem się PS, decimalami i algorytmami. Dzisiaj zachłanny algorytm wydawania reszty z płatności.

function Solve-ChangeMaking 
{
    param(
        [ValidateNotNull()]
        [ValidateRange(0.01, 10000)]
        [decimal]$rest=.99,

        [ValidateNotNullorEmpty()]
        [ValidateScript({ 
        if($_ -lt 0.01 -OR $_ -gt 500)
        {
            throw "Not allowed nominations. Use nomination from 0.01 to  500"
        }
        return $true
        })]
        [array]$nominations =@(.99)
    )

    [array]$returnChange =@()
    [decimal]$remainedRest = $rest 
    $availableNominations = New-Object "System.Collections.Generic.List``1[decimal]"  
    #it should be 'list of deciaml' because problem with removeAt method
    $nominations | % { #adding nominations to list of decimals
        $availableNominations.Add($_)
    }

    $quantity = 0
    while($remainedRest -gt 0)
    {
        [decimal]$max = ($availableNominations | measure -Maximum).Maximum
        $index = $availableNominations.IndexOf($max);
        if($index -lt 0)
        {
            break
        }

        if($remainedRest -ge $max )
        {
            $remainedRest -= $max       
            $returnChange +=$max
            $quantity++
        }
        
        $availableNominations.RemoveAt($index)

        if($availableNominations.Count -eq 0)
        {
            break
        }
    }

    return  New-Object PSObject -Property @{ 
        Quantity  = $quantity; 
        'Remained Rest' =  $remainedRest;
        'Change to return'  = ($returnChange -join ' ');}
}

Do przetestowania algorytmu będziemy chcieli wydać resztę 97 groszy mając do dyspozycji 1, 2, 10, 20 i 50 groszy. Oczywiście zabraknie nam pieniędzy do wydania reszy:
Solve-ChangeMaking -rest .97 -nominations 0.10, 0.20, 0.50, 0.02, 0.01 

I wyniki:

Quantity : 5
Change to return : 0,5 0,2 0,1 0,02 0,01
Remained Rest : 0,14


Albo inny przykład z większą ilością gotówki:
$money = (20, 10, 5, 2, 1, .50, .20, .10, 0.05, .02, 0.01) *5 + 50 + 50
Solve-ChangeMaking -nominations $money -rest (200-0.99) 

A wyniki są następujące:

Quantity : 11
Change to return : 50 50 20 20 20 20 10 5 2 2 0,01
Remained Rest : 0,00

piątek, 28 marca 2014

Demotywacja w pracy

Gdy zastanawiamy się nad motywacją w pracy to pierwsze co nam przychodzi do głowy to pieniądze. Czy tak jest na prawdę??
Chciałbym Ci przedstawić prezentację pewnego doktora psychologii o motywacji.




Autor prezentacji mówi o tym co nas demotywuje do pracy. Jednym z najważniejszych demotywatorów jest powtarzanie tej samej czynności i nie uzyskiwanie rezultatów z tej działalności - podobnie jest z Syzyfową pracą. Jeżeli już uzyskamy wynik naszej pracy to ten rezultat jest niszczony.

Przypominają mi się słowa jakie wypowiedział A. Einstein: Definicja szaleństwa to robienie w kółko tego samego i spodziewanie się odmiennych rezultatów.


Po przeprowadzeniu paru eksperymentów, Pan Dan dochodzi do wniosku, że ignorowanie ludzkiego wysiłku i pracy jaką włożyło się w wykonanie danego zadania jest prawie tak samo złe (demotywujące) jak wyrzucanie rezultatów swojej pracy.


Inne filmiki Dan Ariely można znaleź na jego stronie.

poniedziałek, 24 marca 2014

Pobieranie machine.config z poziomu PS

Machine.config jest bardzo ważnym plikiem. Takie pliki konfiguracyjne jak app.config czy web.config 'dziedziczą' po machine.config. Aplikacja .NET odczytuje ustawienia jakie znajdują się w machine.config, a później z innych plików konfiguracyjnych. Informacje z machine.config są dostępne dla wszystkich aplikacji na komputerze i dlatego mogą zawierać takie informację jak connection string czy informacje o serwerez SMTP.

Jak wiemy PS też jest aplikacją, która korzysta z machine.config. Poniżej zamieściłem skrypt do odczytu informacji z tego pliku:
function Get-MachineConfig
{
    param(
    [switch] $location,
    [switch] $content,
    [switch] $xml,
    [switch] $connectionStrings)

    $retLocation = [System.Runtime.InteropServices.RuntimeEnvironment]::SystemConfigurationFile
    if($location)
    {
        $retLocation 
    }

    $retContent = gc $retLocation
    if($content)
    {
       $retContent 
    }

    $retXml = [xml]$retContent 
    if($xml)
    {
         $retXml
    }

    if($connectionStrings)
    {
       $retXml.configuration.connectionStrings.add
    }
}


Na uwagę zasługuje parametr, który zwraca ścieżkę do używanego pliku konfiguracyjnego:
Get-MachineConfig -location

Oraz parametr do pobierania informacji o dostępnych połączeniach do baz danych:
Get-MachineConfig -connectionStrings

niedziela, 23 marca 2014

Proste sposoby użycia PS akceleratorów

Ostatnio zająłem się akceleratorami w PS. Jeżeli programujesz w PS to przynajmniej z tysiąc razy skorzystałeś z tego udogodnienia. Stosujesz akceleratory jak kastujesz obiekt, tworzysz nowy obiekt albo przy wywoływaniu statycznych metod.
Poniższy kod przedstawi ci jakie masz akceleratory:
$accelerators = [psobject].Assembly.gettype("System.Management.Automation.TypeAccelerators")
$accelerators::get

Oczywiście klasę TypeAccelerators możesz zrobić jako 'accelerator':
#$accelerators::Add('accelerators', $accelerators)
[accelerators]::Get

Takie dodawanie może być przydatne kiedy chcemy deklarować generyczną kolekcje np. listę albo stos:
$stackType = [type]'System.Collections.Generic.Stack`1'
$accelerators::Add('stack', $stackType)
[stack[int]]$stack  = 1,2,3,4

Poniższy przykład jest dla kolejki z intami:
$QueueType = [type]'System.Collections.Generic.Queue`1'
$accelerators::Add('queue', $queueType)
[queue[int]]$queue  = 1,2,3,4

Po wywołaniu można sprawdzić jak te dwie kolekcje zwracają elementy:
$stack #4,3,2,1
$queue #1,2,3,4

Oczywiście można dodać inne klasy, z których mają ciekawe statyczne metody:
$pathType = [type]'System.IO.Path'
$accelerators::Add('path', $pathType)
[path]::GetTempFileName()

Lub tez można dodać klasę Assert, aby testować swój kod:
[System.Reflection.Assembly]::
LoadWithPartialName('Microsoft.VisualStudio.QualityTools.UnitTestFramework')

$assertType = [type]'Microsoft.VisualStudio.TestTools.UnitTesting.Assert'
$accelerators::Add('assert', $assertType)

Sprawdzenie asserci wygląda następująco:
$actualIntValue= 11
[assert]::AreEqual(12,$actualIntValue)

piątek, 21 marca 2014

Czas generowania zmiennej losowej w PS

Ostatnio sprawdzałem szybkość działania polecenia do generowania zmiennych losowych z przedziału (0,1).
Sprawdzimy wywołanie zmiennych losowych 1000 razy i zapisanie tych zmiennych do tablicy:

$count =1000
$num1= @()
$num2= @()


Poniżej kod do sprawdzenia obiektu z klasy random:
$objRandom = new-object random
$measure1 = Measure-Command -Expression {
            for ($i =0; $i -lt $count; $i++)
            {
              $num1 += $objRandom.NextDouble()
            }
}

I w przypadku polecenia Get-Random (alias random):
$measure2 = Measure-Command -Expression {
            for ($i =0; $i -lt $count; $i++)
            {
               $num2 += (random)/[Int32]::MaxValue 
            }
}


Wynik czasu działania jednego z kilku wywołań:

$measure1: 
TotalSeconds      : 0,3785933
TotalMilliseconds : 378,5933

$measure2:
TotalSeconds      : 56,7377659
TotalMilliseconds : 56737,7659
Wynik mnie nie zaskoczył. Spowolnienie może mieć kilka powodów:
- korzystanie z aliasu
- polecenia PS są wolniejsze niż wywołania obiektów .NET
- przy każdym wywołanieu polecenia radnom, tworzy się obiekt, a w pierwszym przypadku obiekt już jest zainicjalizowany
- dodatkowa operacja dzielenie liczby losowej przez maksymalną liczbę int

czwartek, 20 marca 2014

Wielkość folderów w PS

Zastanawiałeś się jak można sprawdzić wielkość folderów w podkatalogach? Poniżej jest kod w PS, który wyświetla wielkość folderów w podkatalogach.

function Show-SpaceUsage
{
    [CmdLetBinding()]
    param(
        [Parameter(Mandatory=$true, Position=0)]  
        [ValidateScript({Test-Path $_ })]
        [string]$path,

        [decimal]$parentSize=0,

        [ValidateNotNullOrEmpty()]
        [string]$format = "N2"
    )
    $size = (Get-ChildItem $path -Recurse | ?{$_.PSIsContainer -eq $false} | measure -property length -sum).sum
   
    $parentPercentage =1
    if($parentSize -ne 0)
    {
       $parentPercentage = $size / $parentSize  
    }

    $item = gi $path
    new-object PSObject | 
            Add-Member -PassThru -MemberType NoteProperty -Name  Path  -Value $item.FullName |
            Add-Member -PassThru -MemberType NoteProperty -Name  Name  -Value $item.Name |
            Add-Member -PassThru -MemberType NoteProperty -Name  GB  -Value ("{0:$format}" -f ($size / 1GB)) |
            Add-Member -PassThru -MemberType NoteProperty -Name  MB  -Value ("{0:$format}" -f ($size / 1MB)) |
            Add-Member -PassThru -MemberType NoteProperty -Name  KB  -Value ("{0:$format}" -f ($size / 1KB)) |
            Add-Member -PassThru -MemberType NoteProperty -Name  "Percentage of Parent" -Value ("{0:$format}" -f $parentPercentage)

    $dirs =  Get-ChildItem $path | ?{$_.PSIsContainer -eq $True} | sort Name
    $dirs | %{
        Show-SpaceUsage -path $_.FullName -parentSize  $size
        Write-verbose ""
    }
}

Wyniki są sformatowane do tabeli:
Show-SpaceUsage -path "." -Verbose | Format-Table

Oczywiście można wyniki wyeksportować do csv i tam już posortować po największych folderach:
Show-SpaceUsage -path "." | Export-Csv -path "result.csv" -Delimiter ';' -NoTypeInformation -Encoding ASCII 

niedziela, 16 marca 2014

Sprawdzanie wolnej przestrzeni dyskowej w PS

Czy znasz może sposób na pobranie informacji o wolnych miejscach na dysku za pomocą PS?? Jedyna możliwość na uzyskanie tej informacji jest skorzystanie z WMI. Poniżej jest skrypt:
function Get-FreeSpace(
[string]$drive,
[string]$computer="localhost")
{
 ([wmi]"\\$computer\root\cimv2:Win32_logicalDisk.DeviceID='$drive'").FreeSpace
}
I możemy wykonać:
$freeSize = Get-DiskSpace -drive "E:"
Do tego wyniku możemy zastosować dzielenie przez jednostki przestrzeni pamięci:
PS>$freeSize
42215112704

PS>$freeSize / 1MB
40259,46875

PS>$freeSize / 1GB
39,3158874511719

Jeżeli byś znalazł inny sposób niż użycie WMI to daj znać

sobota, 15 marca 2014

WhoAmI - pierwsze polecenie na nowym komputerze

Jednym z pierwszych poleceń PowerShell wywołanych na Twoim nowym komputerze powinno być polecenie WhoAmI:
PS>WhoAmI 
<computer_name>\<user_name>
Można uzyskać listę wszystkich grup do których należysz (do tego możesz wyeksportować do csv'ki):
WhoAmI /GROUPS /FO CSV /NH
Oraz możesz uzyskać dodatkową listę uprawnień:
PS>WhoAmI /PRIV

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                           State   
============================= ===================================== ========
SeShutdownPrivilege           Zamknij system                        Disabled
SeChangeNotifyPrivilege       Obejd« sprawdzanie przy przechodzeniu Enabled 
SeUndockPrivilege             Usuä komputer ze stacji dokujĄcej     Disabled
SeIncreaseWorkingSetPrivilege Zwi©ksz zestaw roboczy procesu        Disabled
SeTimeZonePrivilege           Zmieä stref© czasowĄ                  Disabled

Wszystko w porządku pod warunkiem, że posiadasz system nowszy niż Windows XP. Jeżeli jednak nie posiadasz takiego systemu to będziesz musiał sam napisać taki program/skrypt. Na przykład w PowerShellu będzie wyglądał tak:
function WhoAmIPS
{
 [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
}

Aby uzyskać listę wszystkich grup do których należysz to powinieneś wywołać poniższą funkcję:
function WhatGroupAmIIn
{
    $user = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $nt = "System.Security.Principal.NTAccount" -as [type]
    $user.Groups | 
    ForEach-Object { $_.translate($NT)}
}

piątek, 14 marca 2014

Problem Józefa Flawiusza w PowerShell cz.2

Już wcześniej pisałem o problemie Józefa Flawiusz. W tamtych czasach nie miałem rozwiązania przedstawiającego wszystkie iteracje tylko rozwiązanie zwracało ostatni element. Napisałem od nowa algorytm do wyświetlenia wszystkich elementów.
Poniżej jest funkcja iteracyjna do wyświetlenia kolejności elementów:
function Iterate-JosephusFlavius(
        [ValidateRange(1,1000)]
        [int]$killEvery = 3,
        [ValidateRange(1,1000)]        
        [int]$soldiers = 41
      )
{
    [bool[]]$bools = @($false)*$soldiers
    $indexOfKilledSoldiers=@()
    $soldiersToKill = $soldiers
    $rest = 0
    $setps=0
    while($soldiersToKill  -gt 0)
    {
        for($i=$rest; $i -lt $bools.Length;$i++)
        {
            if($bools[$i] -eq $false)
            {
                $setps++
                if($setps -eq $killEvery)
                {
                    $bools[$i] = $true
                    $indexOfKilledSoldiers += $i
                    $soldiersToKill--
                    $setps=0
                }
            }
        }
        $rest = $i -$bools.Length
    }
    return $indexOfKilledSoldiers
}
A tutaj jest funkcja rekurencyjna. Z tą funkcją miałem więcej problemów. Niestety argumenty funkcji są inne dla iteracji jak i dla rekurencji. Jak będę miał chwilkę czasu to poprawię tą różnicę.
function Recursive-JosephusFlavius(
        [ValidateRange(1,1000)]
        [int]$killEvery = 3,
        [int[]]$soldiers,
        [int[]]$killedSoldiers=@(),
        [int]$prevRest = 0
      )
{
    if($soldiers.Length -eq 0 )
    {
        return $killedSoldiers
    }
    $aliveSoldiers=@()
    
    if($soldiers.Length - $killEvery -lt 0)
    {
        $soldierToKill = (($killEvery + $prevRest) -1) % $soldiers.Length 
        for($i=0; $i -lt $soldiers.Length; $i++)
        {
            if($soldierToKill -eq $i)
            {
                $killedSoldiers += $soldiers[$i]
            }else
            {
                $aliveSoldiers += $soldiers[$i] 
            }
        }  
    }
    else
    {
        for($i=0; $i -lt $soldiers.Length; $i++)
        {
            if(($i+1)  % $killEvery -ne 0)
            {
                $aliveSoldiers +=   $soldiers[($i-$prevRest)]  
            }else
            {
                $killedSoldiers +=  $soldiers[($i-$prevRest)]
            }
        }
        $soldierToKill = $soldiers.Length % $killEvery
    }
   return  Recursive-JosephusFlavius -soldiers $aliveSoldiers -killEvery $killEvery -killedSoldiers $killedSoldiers  -prevRest $soldierToKill 
}

Wywołanie tych funkcji oraz sprawdzenie działania wygląda następująco:
$soldiers = 0..40 #41 soldiers
$killEvery = 3

$res1= Recursive-JosephusFlavius -soldiers $soldiers -killEvery $killEvery
$res2 = Iterate-JosephusFlavius  -soldiers ($soldiers.Length) -killEvery $killEvery

for($i=0; $i -lt [math]::Max($res1.Length,$res2.Length); $i++)
{
    if($res1[$i] -ne  $res2[$i])
    {
        throw "not the same"
    }
    else
    {
        Write-Host $res1[$i]
    }
}

środa, 12 marca 2014

Pobieranie z GAC za pomocą PowerShell

Tak się zastanawiam, dlaczego PowerShell nie ma możliwości pobrania danych z GAC. PS wymaga zainstalowanego .NET, a najnowsze systemy z automatu mają ten framwork zainstalowany. Niestety jesteśmy zmuszeni do używania GacUtil.exe (Global Assembly Cache Tool). Napisałem skrypt do zwracania elementów z GACy.

function Get-GAC
{
    [CmdletBinding()]
    param()

& 'C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\gacutil.exe' /L |
? { $_ -match '^  ([\w\.]+,.*)$' } |
    % { 
        $fullName = ($Matches[1])
        try
        {
           $assembly = [System.Reflection.Assembly]::Load($fullName)
           $assembyName = $assembly.GetName()

           new-object PSObject -prop @{ 
                FullName = $assembly.FullName; 
                GAC = $assembly.GlobalAssemblyCache;
                Version = $assembly.ImageRuntimeVersion;
                FullyTrusted = $assembly.IsFullyTrusted;
                Location = $assembly.Location;
                Name = $assembyName.name;
                ReferencedAssemblie = $assembly.GetReferencedAssemblies();
                Token = ($assembyName.GetPublicKeyToken() |  foreach {$_.ToString("X2")}) -join '';
                HashAlgorithm = $assembyName.HashAlgorithm;
                CultureInfo = $assembyName.CultureInfo;
           }
        }
        catch [system.exception]
        {
            Write-Verbose $_
        }
    }| sort Name -unique 
}

Przykład użycia jest poniżej. Dodatkowo dodajemy parametr verbose, gdyż nie wszystkie biblioteczki są załadowywane. Jeszcze troszkę będę musiał przy tym posiedzieć i sprawdzić dlaczego nie wszystko zostaje załadowane. Może ty masz jakiś pomysł??
( Get-GAC -Verbose ) | out-gridview

poniedziałek, 10 marca 2014

Szybka implementacja WhatIf i Confirm w PS

Kiedyś nie wiedziałem o parametrach SupportsShouldProcess i ConfirmImpact. Z powodu tej niewiedzy ręcznie implementowałem WhatIf i Confirm za pomoca [switch] w parametrze.

Ten poniższy kod jest bardziej dla mnie niż dla kogoś innego, ponieważ teraz prawie zawsze staram się używać tych dwóch parametrów, a zawsze zapominam sobie o tych nazwach :)

function Get-CmdletJson
{
     [cmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] 
     param()
    
    if($PSCmdlet.ShouldProcess(' $PSCmdlet'))
    {
        $PSCmdlet | ConvertTo-Json
    }
}

Kiedy wywoła się polecenie z parametrem WhatIF to wyświetli się poniższy komunikat:
Get-CmdletJson -WhatIf
#What if: Performing the operation "Get-CmdletJson" on target " $PSCmdlet".

W przypadku Conform to potwierdzenie jest uzależnione od hosta
Get-CmdletJson -Confirm

Poniższy rysunek przedstawia 2 różne host. Małe okienko potwierdzenia należy do PowerShell ISE, a duże okienko tekstowe należy do PowerShell ConsoleHost:



piątek, 7 marca 2014

MergeSort i Assert w PowerShell

Chciałem napisać prostą asercje i poćwiczyć sortowanie, więc napisałem script, z którym podzielę się z tobą :)
Na samym początku będziemy potrzebować funkcje, która będzie łączyła lewą i prawą stronę tablicy. Te części są posortowane.
function Merge
{
    param([array]$A,
    [array]$temp=@(),
    [int]$low=0,
    [int]$high=$($A.Length-1),

    [int]$middle = 
$([math]::Floor($low + ($high - $low)/2))
    )
    
    $i = $low
    $j = $middle +1
    
    Assert-Command {IsSorted ($A[$i..$middle]) }
    Assert-Command {IsSorted ($A[$j..$high]) }

    $temp = $A
    $swapA = @(0)* $A.Length
    for($k = $low; $k -le $high; $k++)
    {
        if( $i -gt $middle)
        {
            $swapA[$k] = $temp[$j]
            $j++        
        }
        elseif ($j -gt $high)
        {
            $swapA[$k] = $temp[$i]
            $i++
        }
        elseif( $temp[$j] -gt $temp[$i] )
        {
            $swapA[$k] = $temp[$i]
            $i++
        }
        else
        {
            $swapA[$k] = $temp[$j]
            $j++
        }
    }
    $sortedA =$swapA[$low..$high]

    Assert-Command {IsSorted $sortedA}
    return $sortedA
}
Napiszemy prostą funkcję do sprawdzenia czy elementy są posortowane:
function IsSorted([array]$arr)
{

    for($i=0; $i -lt $arr.Length-1; $i++)
    {
        if($arr[$i] -gt $arr[$i+1])
        {
            return $false
        }
    }
    return $true
}

Do funkcji scalania potrzebna jest funkcja asercji:
function Assert-Command
{
    param(
    [Parameter(Position=0,Mandatory=$true)]
    [ScriptBlock]$condition,

    [Parameter(Position=1)]
    [string]$message
    )
    $invoke = $MyInvocation

    try{
        $ErrorActionPreference = "STOP"
        $result = &$condition
    }catch{
        $result=$false
        $exceptionType = $_.Exception.GetType().FullName
    }


    if(-not($result))
    {
        $errMess = "Assert failed in line: $($invoke.Line) `n"
        $errMess += "Message: $message `n"
        $errMess += "Exception type: $exceptionType `n"
        $errMess += "Used parameters: $($invoke.BoundParameters.condition) `n"
        throw $errMess
    }
}
I na końcu potrzebujemy funkcji sortując:
function MergeSort-Object
{
    param([array]$A,
    [array]$temp=@(),
    [int]$low=0,
    [int]$high=$($A.Length-1),

    [int]$middle = $([math]::Floor($low + ($high - $low)/2))
    )
    
    if($low -ge $high)
    {
        return $A[$low]
    }

    [array]$sortedPart1 = MergeSort-Object -A $A -low $low -high $middle
    $A = Fill-Array -table $A -with $sortedPart1 -offSet $low

    [array]$sortedPart2  = MergeSort-Object -A $A -low ($middle+1) -high $high
    $A = Fill-Array -table $A -with $sortedPart2 -offSet ($middle+1)

    [array]$sorted = Merge -A $A -low $low -middle $middle -high $high
    return $sorted 
}
Niestety będziemy musieli użyć pomocniczej funkcji do wypełnienia tablicy już posortowanej części. Są to uroki przekazywania parametrów funkcji przez wartość, a nie przez referencje.
function Fill-Array
{
    param([array]$table,
    [int]$offSet=0,
    [array]$with
    )

    [array]$newTable = $table
    
    for($i =0; $i -lt $with.Length; $i++)
    {
        
        $newTable[$offSet + $i] = $with[$i]
    }
    return $newTable
}
I przykładowe sortowanie tablicy wygląda następująco:
 MergeSort-Object -A 1,3,7,99,5,3,32,5,11,91 

Kolejność wykonywania działań w PS


Już wcześniej pisałem o problemach kolejności wykonywania działań w PS, ale tym razem chciałem rozszerzyć ten przykład o kolejne przypadki:

Write-host " 2 + 3 * 4             is  $(2 + 3 * 4 )"
Write-host " `"2`" + 3 * 4         is  $("2" + 3 * 4     )"
Write-host " 2 + `"3`" * 4         is  $(2 + "3" * 4     )"
Write-host " 2 + 3 * `"4`"         is  $(2 + 3 * "4"     )"
Write-host " `"2`" + `"3`" * 4     is  $("2" + "3" * 4   )"
Write-host " `"2`" + 3 * `"4`"     is  $("2" + 3 * "4"   )"
Write-host " 2 + `"3`" * `"4`"     is  $(2 + "3" * "4"   )"
Write-host " `"2`" + `"3`" * `"4`" is  $("2" + "3" * "4" )"

Wynik takiego wywołania są następujący:

2 + 3 * 4 is 14
"2" + 3 * 4 is 212
2 + "3" * 4 is 3335
2 + 3 * "4" is 14
"2" + "3" * 4 is 23333
"2" + 3 * "4" is 212
2 + "3" * "4" is 3335
"2" + "3" * "4" is 23333

Zawsze sprawdzaj jaki typ danych masz po lewej stronie działania!!

wtorek, 4 marca 2014

Listy, słowniki, tuple i domyślne wartości w Power Shell

Tworzenie generyków w PS jest tak ciężkie i nie przyjemne w pisaniu, że ludzie nie używają ich. Postanowiłem coś z tym zrobić i napisałem funkcje, które tworzą obiekty generyczne:
function New-Generic
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$type,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string[]]$typeParameter,

    [Parameter(Mandatory=$false, Position=3)] 
    [array]$constructorParameters=$null
    )

    $genericType = [type]($type + ‘`’ + $typeParameter.Count)
    [type[]] $tParameters = $typeParameter
    $closedType = $genericType.MakeGenericType($tParameters)

    Write-Verbose "Creating object $closedType with parameters $constructorParameters" 
    return ,([Activator]::CreateInstance($closedType, [Object[]]$constructorParameters))
}
Najwięcej czasu w napisaniu tej funkcji zajęło mi dodanie przecinka po słowie return. Ponad jeden dzień spędziłem w szukaniu dlaczego kastuje mi do zwyczajnego obiektu. Ten przecinek robi jakąś magię :)

Powyższa funkcja tworzy obiekt, który jest generyczny. Postanowiłem owrappować tą funkcję z najczęściej używanymi kolekcjami generycznymi:
function New-List
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )
   return ,( New-Generic -type 'System.Collections.Generic.List' -typeParameter $typeParameter )
}


function New-LinkedList
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )
   return ,( New-Generic -type 'System.Collections.Generic.LinkedList' -typeParameter $typeParameter )
}

function New-Queue
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )
   return ,( New-Generic -type 'System.Collections.Generic.Queue' -typeParameter $typeParameter )
}

function New-SortedSet
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )
   return ,( New-Generic -type 'System.Collections.Generic.SortedSet' -typeParameter $typeParameter )
}

function New-Stack
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )

   return ,( New-Generic -type 'System.Collections.Generic.Stack' -typeParameter $typeParameter )
}

function New-HashSet
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )

   return ,( New-Generic -type 'System.Collections.Generic.HashSet' -typeParameter $typeParameter )
}

function New-Dictionary
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$keyType,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string]$valueType
    )
    return ,(New-Generic -type 'System.Collections.Generic.Dictionary' -typeParameter $keyType,$valueType)
}

function New-SortedDictionary
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$keyType,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string]$valueType
    )
    return ,(New-Generic -type 'System.Collections.Generic.SortedDictionary' -typeParameter $keyType,$valueType)
}

function New-SortedList
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$keyType,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string]$valueType
    )
    return ,(New-Generic -type 'System.Collections.Generic.SortedList' -typeParameter $keyType,$valueType)
}

function New-KeyValuePair
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$keyType,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string]$valueType
    )
    return ,(New-Generic -type 'System.Collections.Generic.KeyValuePair' -typeParameter $keyType,$valueType)
}
Przykład użycia dla listy jest poniżej. Można zauważyć rzucenie wyjątku kiedy będziemy chcieli dodać obiekt do listy, który nie może być kastowany do int:
$list = New-List "int" -Verbose
$list.GetType() 
$list.Add(1)
$list.Add('2')
$list.Add("three") #throw exception
Również można zainicjalizować stos doubli:
$stack= New-Stack -typeParameter 'double'
$stack.getType()
$stack.Push(1)
$stack.Push(20000)
$stack.Push(-1.123)
$stack.Pop()
A tworzenie słownika wygląda następująco:
$dic = New-Dictionary -keyType 'string' -valueType 'int'
$dic.Add("one",1)
$dic.Add("two",2)
Zanim stworzy się funkcję dla tupli to będzie potrzebna funkcja zwracająca domyślne wartości:
function Get-DefaultValue
{
    param(
    [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
    [type]$type
    )
    process{

        if($type.IsValueType)
        {
            return [Activator]::CreateInstance($type)
        }
        return $null
    }
}

Wynik dla boola:
Get-DefaultValue 'bool' #False
A funkcja do tworzenia tupli jest następująca:
function New-Tuple
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string[]] $typeParameter ,

    [Parameter(Mandatory=$false, Position=1)] 
    [array]$constructorParameters
    )

    if(-not($constructorParameters))
    {
        $constructorParameters = $typeParameter |Get-DefaultValue

    }
   return ,( New-Generic -type 'System.Tuple' -typeParameter $typeParameter -constructorParameters $constructorParameters )
}
Oczywiście można stworzyć tuple dla dowolnej (o ile pozwoli konstruktor) ilości parametrów
$tuple3= new-Tuple -typeParameter 'int', 'object', 'string'

$tuple3.GetType();
$tuple3.Item1

$tuple3= new-Tuple -typeParameter 'int', 'bool', 'string' -constructorParameters 1,$true, "SuperString"
$tuple3.Item2
$tuple3.Item3