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