PowerShell okiem programisty .NET

Artykuł ten tworzę z dość prostego powodu. Wraz z mijającym czasem w każdym programiście rośnie opór oraz lenistwo wobec powtarzalnych czynności wykonywanych codziennie w pracy. Stąd też szukamy sposobów na zautomatyzowanie choć części z tych zadań. PowerShell może być jednym ze sposobów na osiągnięcie tego celu. Z tego powodu warto lepiej się z nim zapoznać.

Zanim zacznę, chcę zaznaczyć, że nie jestem mistrzem PS. Postanowiłem jednak podzielić się swoimi doświadczeniami z punktu widzenia typowego programisty .NET i w miarę zwięźle pokazać, w jaki sposób przenieść niektóre z najpopularniejszych operacji występujących w tym świecie na ich odpowiedniki w PowerShellu. Zaczynajmy.

Rzecz, którą nieraz warto zrobić przed rozpoczęciem właściwej pracy, to uruchomienie PowerShella w trybie administratora. Możemy to zrobić bezpośrednio z poziomu cmd.exe za pomocą komendy:powershell “Start-Process powershell -Verb runAs”.

Wypisanie textu na konsolę

C#:

System.Console.WriteLine("Hello world!");

PowerShell:

Write-Host "Hello world!"
# lub krócej
"Hello world!"

Zmienne

C#:

int a = 1, b = 2;
var a = 1; var b = 2;

PowerShell:

$a = 1
$b = 2
$a, $b = 1, 2    # Python-like

Tablice

C#:

var array = new object[] { 1, "text", 3.14 };
var a = array[0];
var iter = Enumerable.Range(1, 10);

PowerShell:

$array = 1, "text", 3.14
$array = @(1, "text", 3.14) # jawny zapis
$a = $array[0]
$iter = (1..10)

Słowniki

C#:

var dictionary = new Dictionary<string,string>
{
    {"key 1", "value 1"},
    {"key 2", "value 2"}
};
dictionary["key 1"]

PowerShell:

$dictionary = @{"key 1" = "value 1"; "key 2" = "value 2"}
$dictionary["key 1"]

Operatory logiczne, warunkowe i binarne

C#:

true == true    // równy
true != false   // różny
1 > 0           // większy
0 < 1           // mniejszy
1 >= 0          // większy lub równy
0 <= 1          // mniejszy lub równy
!true           // negacja
true && true    // koniunkcja (logiczny)
true || false   // alternatywa (logiczny)
1 ^ 1           // alternatywa wykluczająca 
1 | 1           // alternatywa (binarny)
1 & 1           // koniunkcja (binarny)

PowerShell:

$True -eq $True    # równy
$True -ne $False   # różny
1 -gt 0            # większy
0 -lt 1            # mniejszy 
1 -ge 0            # większy lub równy
0 -le 1            # mniejszy lub równy
-not $True         # negacja
$True -and $True   # koniunkcja (logiczny)
$True -or $False   # alternatywa (logiczny)
$True -xor $False  # alternatywa wykluczająca (logiczny)
1 -bxor 2          # alternatywa wykluczająca (binarny)
1 -bor 1           # alternatywa (binarny)
1 -band 1          # koniunkcja (binarny)

Instrukcje warunkowe

C#:

    if($a == true)
    {
      Console.WriteLine("Statement 1");
    }
    else if($b == true)
    {
      Console.WriteLine("Statement 2");
    }
    else
    {
      Console.WriteLine("Statement 3");
    }

PowerShell:

If($a -eq $True) {
    "Statement 1"
} ElseIf($b -eq $True) {
    "Statement 2"
} Else {
    "Statement 3"
}

Pętle

C#:

for(int i=0; i < 10; i++)
    Console.WriteLine(i);

foreach(var element in array)
    Console.WriteLine(element);

while(i < 10)
    Console.WriteLine(i++);

do {
    Console.WriteLine(i++);
} while(i < 10);

PowerShell:

for($i=1; $i -lt 10; $i++) {
    Write-Host $i
}

foreach($i in (1..10)) {
    Write-Host $i
}

while($i -lt 10) {
    Write-Host $i
    $i++
}

do {
    Write-Host $i
    $i++
} while($i -lt 10)

do {
    Write-Host $i
    $i++
} until($i -eq 10)   # odpowiednik: do {} while(-not ($i -ne 10))

Definicje własnych metod/funkcji

C#:

// C# wymaga "opakowania funkcji" w klasę
class A
{
    public int Fib(int n = 1)
    {
        if(n < 2) return n;
        return Fib(n - 2) + Fib(n - 1);
    }
}
(new A()).Fib(10);    // wynik: 55

PowerShell:

Function Fib($n = 1) {
    if($n -lt 2) {
        return $n
    }
    return ((Fib($n - 2)) + (Fib($n - 1)))
}
Fib(10)    # wynik: 55

Wywołania statycznych klas .NET

C#:

System.DateTime.Parse("10-01-2012");    // 1 Października 2012

PowerShell:

[System.DateTime]::Parse("10-01-2012");   # 1 Października 2012

Tworzenie nowych obiektów za pomocą konstruktorów

C#:

var date = new DateTime(2012, 10, 1);    // 1 Października 2012

PowerShell (przestrzeń nazw System jest domyślnie zaimportowana):

$date = New-Object DateTime(2012, 10, 1)   # 1 Października 2012

Kilka przydatnych cmdletów

Na koniec chciałbym pokazać kilka cmdletów, które nieraz ułatwiają pracę. Warto zauważyć, że nieraz cmdlet dostępny jest również poprzez alias (przeważnie więcej niż jeden). Niektóre z nich zostały tutaj przedstawione.

"A = {0} hours {1} minutes" -f 10, 20  # odpowiednik string.Format

#tworzenie nowego pliku/katalogu
New-Item c:\files\new_file.txt -Type File
New-Item "c:\directories" -Type Directory
ni c:\files\new.txt -type file # skrótowy zapis

# kopiujemy wszystkie pliki z c:\files do c:\directories
Copy-Item c:\files\* c\directories
copy c:\files\* c\directories    # skrótowy zapis
cp c:\files\* c\directories      # skrótowy zapis

# usunięcie pliku lub katalogu
Remove-Item c:\files\new.txt
rm c:\files\new.txt    # alternatywa

# przenoszenie plików lub folderów
Move-Item c:\files\old.txt c:\files2\
move c:\files\old.txt c:\files2\    # skrótowy zapis
mv c:\files\old.txt c:\files2\      # skrótowy zapis

# pobranie zawartości pliku
Get-Content c:\files\file.txt

# wpisanie tekstu do pliku
Set-Content file.txt "file content"
"file content" | Out-File file.txt

# sprawdzenie istnienia pliku lub folderu pasującego do podanego wzorca
Test-Path c:\files\*.txt

# wyszukanie linijki z pliku źródłowego, zawierającego szukaną frazę
Get-Content file.txt | Select-String "fraza"
# zwaraca True, jeżeli znaleziono dopasowanie
Get-Content file.txt | Select-String "fraza" -quiet
# rozróżnienie wielkości liter
Get-Content file.txt | Select-String "fraza" -casesensitive

Get-Date    # pobieramy bierzącą datę z zegara systemowego: DateTime.Now
Get-Date -displayhint date   # jak powyżej, ale pobieramy samą datę: DateTime.Today
Get-Date -displayhist time   # analogicznie dla czasu

# ustawia zegar systemowy na hh:mm:ss do przodu (w przykładzie 1h10m)
Set-Date -adjust 1:10:00

# informacje o procesie
Get-Process process1, process2    # jawne wyliczenie szukanych nazw
Get-Process proc*                 # szukanie wg. wzorca
ps proc*                          # skrótowy zapis


# zakończ process(-y)
Stop-Process -processname process*   # zakończ procesy o nazwie zgodnej z wzorcem
kill 3888          # skrótowy zapis, zakończ process o Id równym 3888

PS: W niektórych z powyższych komend wykorzystałem jeden z feature’ów PowerShella, którego wcześniej nie opisałem – pipeline operator. Jest on reprezentowany przez symbol | i służy do przekazywania wyniku jednej funkcji/polecenia jako wejścia do następnej.