Një vegël e dobishme me PowerShell


Gjatë punës sime të përditëshme, shpesh me duhet të punoj me objekte me shumë veçori (property). Zakonisht, sa me kompleks projekti në të cilin punoj, aq më komplekse dhe më të shumëta veçoritë e objekteve. Një shembull i tillë me 62 veçori në imazhin më poshtë:


Për objekte të këtilla më duhet të shkruaj kod në C#, HTML, Razor, JavaScript e plot tjerë formate. Po ta bëja me dorë, do të humbja shumë kohë, por edhe do të rrezikoja shumë gabime gjatë shkrimit.

Për t'i ikur këtyre problemeve e përdori një skript të shkruar në PowerShell. Nëse nuk keni pasur rast të njiheni me PowerShell, ky është emri i një gjuhe programuese që vjen me çdo instalim të Windows 10. 
Për shijen time, PowerShell ka një sintaksë "të fëlliqur", por kjo është diçka subjektive. Ajo që është shumë më me rëndësi, është se PowerShell është shumë praktik, shumë i gjithanshëm dhe me të vërtetë shumë i fuqishëm. Jam i sigurt se ka edhe gjuhë tjera që janë poaq e ndoshta edhe më të fuqishme se PowerShell, por unë nuk kam pasur rast të njihem me to, pra vetëm këtë e njoh dhe përdori. 

Skriptën që e përdori e kam quajtur Transform.ps1 dhe e kam vendosur bashkë me dy fajla tjerë tekstual, input.txt dhe templates.txt dhe një nën-direktorium results në një direktorium në PC-në tim.





Në fajlin input.txt i shkruaj veçoritë në formë të rreshtave. Secili rresht fillon me emrin e veçorisë, pastaj vjen një karakter bosh (interval) dhe më pas pason lloji (type) i veçorisë. Për ilustrim nuk do ta marr shembullin gjigant nga larg po vetëm disa rreshta, parimi mbetet i njëjti:

FirstName string
LastName string
Age int
LastLogin DateTime


Fajli templates.txt përmban kallëpet (template) të cilat më nevojiten gjatë punës. Përsëri për shkak të hapësirës së kufizuar këtu, do të marr një shembull më të shkurtër se fajli aktual i imi.

<< HTML 
:Form-Control
<div class="form-group">
    <label>{0}</label>
    <input type="text" class="form-control" @bind="@{0}" />
</div>

<< C#
:C# Short Private Field 
private {1} {0};

:C# Short Form Property
public {1} {0} { get; set; }

:C# Full Property
private {1} _{0};
public {1} {0} {
  get{
    return _{0};
  }
  set { 
    _{0} = value;
  }
}



Edhe këtu lexohen rreshtat me radhë.
  • Rreshtat që fillojnë me "<<" janë komentime për mua dhe nuk përfillen nga skripta. 
  • Rreshtat që fillojnë me ":" shënojnë fillimin e një kallëpi të ri. Emri i kallëpit të ri është teksti që vjen pas ":". Duhet pasur kujdes të mos përdoret i njëjti emër më shumë se një herë.
  • Rreshtat vijues (deri tek rreshti me ":" apo fundi i fajlit) janë rreshtat që definojnë kallëpin përkatës.
  • dhe së fundi, rreshtat bosh nuk përfillen, por shërbejnë për një ndarje më estetike të definicioneve të kallëpeve.

Pra në imazhin më lartë, se pari vjen një koment me tekstin HTML, pastaj vjen një kallëp i ri me emrin "form-control" dhe definicionin:

<< HTML 
:Form-Control
<div class="form-group">
    <label>{0}</label>
    <input type="text" class="form-control" @bind="@{0}" />
</div>

<< C#
:C# Short Private Field 
private {1} {0};

:C# Short Form Property
public {1} {0} { get; set; }

:C# Full Property
private {1} _{0};
public {1} {0} {
  get{
    return _{0};
  }
  set { 
    _{0} = value;
  }
}
dhe pastaj pasojnë definicionet tjera. {0} dhe {1} janë vendet (placeholder) ku do të shkruhen emri, përkatësisht lloji i veçorisë së radhës. Në shembullin tonë {0} do të zëvendësohet me emrin "FirstName" dhe rezultati do të jetë:

<div class="form-group">
    <label>FirstName</label>
    <input type="text" class="form-control" @bind="@FirstName" />
</div>
po të kishim marrur kallëpin e katërt (C# Full Property ), atëherë rezultati do t'ishte:

private string _FirstName;
public string FirstName {
  get{
    return _FirstName;
  }
  set { 
    _FirstName = value;
  }
}
Ja edhe teksti I skriptës transform.ps1:

[CmdletBinding()]
param (
    [Parameter()]
    [string]
    $templateName 
)


function Transform {
    param (
        [Parameter()]
        [string]
        $templateName 
    )

    Begin { }

    Process {

        # lexo permbajtjen e .\templates.txt. 
        # $templatesText eshte nje array i stringave
        $templatesText = Get-Content .\templates.txt

        $name = ""
        $templates = @{ } # ketu do te ruhen 'kallëpet'
        $content = "" # permbajtja e kallepit te radhes

        # itero
        for ($i = 0$i -lt $templatesText.Count$i++) {
            $row = $templatesText[$i# rreshti i radhes

            # rresht komentimi, injoroje
            if($row.StartsWith("<<")) { Continue } 

            # fillon me :
            # , atehere eshte fillim i kallepit te ri 
            # dhe permban emrin e tij
            if ($row.StartsWith(":")) {  

                # iki heren e pare kur $name te jete ende 
                # i zbrazet
                if (-not [string]::IsNullOrEmpty($name)) { 
                    $templates.Add($name$content)
                    # pastro permbajtjen per ta pranuar 
                    # permbajtjen e re
                    $content = "" 
                }

                # emri i kallepit
                # te radhes
                $name = $row.Substring(1
                continue;
            }   
            else {
                # shtoja kallepit te radhes
                $content += $row + "`r`n" 
            }
        }

        $templates.Add($name$content# shto kallepin e fundit

        
        # paraqit opcionet ...
        $counter = 0
        $templates.Keys | ForEach-Object {
            "$($counter). $($_)"
            $counter += 1
        }

        Write-Output "`n"

        # ... dhe lexo opcionin e zgjedhur nga perdoruesi
        $choice =  Read-Host  -Prompt "Zgjidh kallepin e duhur"
        # emri i kallepit
        $templateName = @($templates.Keys)[$choice]
        # permbajtja e kallepit
        $template = $templates[$templateName]
        
        # lexo te dhenat hyrese        
        $itemRows = Get-Content .\input.txt |  Where-Object { 
            -not [string]::IsNullOrEmpty($_
        } 
        
        # nese direktoriumi .\results nuk ekziston, atehere krijoje
        [System.IO.Directory]::CreateDirectory(".\results"

         # pergadite adresen e fajlit te rezultateve
        $resultPath = ".\results\" + 
            [System.DateTime]::Now.ToString("yyyyMMdd-HHmmss") + 
            ".txt"

        # itero, gjenero tekstet e transformuara dhe ne fund 
        # ruaje rezultatin ne fajlin e rezultateve
        $itemRows | ForEach-Object { 
            $item = $_ -split " "

$result = $template -replace "\{0\}"$item[0
            $result =  $result -replace "\{1\}"$item[1]
            # emitoje vleren ne $result
            $result
        } | Out-File -FilePath $resultPath -Encoding utf8 

        # hape fajlin e rezultateve
        explorer.exe $resultPath 

    }

    End { }
        
}

Transform -templateName $templateName
Skripta mund të startohet me klikim të dyfishtë me pullën e majtë të miut, nga consola e Windows 10:

C:\...\Templates>transform.ps1
0. Form-Control
1. C# Short Private Field
2. C# Short Form Property
3. C# Full Property


Zgjidh kallepin e duhur: 2



apo direkt nga konsola e PowerShell:


PS C:\ ... \Templates> .\transform.ps1
0. Form-Control
1. C# Short Private Field 
2. C# Short Form Property
3. C# Full Property


Zgjidh kallepin e duhur:



Me atë rast, paraqitet lista e kallëpeve të gjetura ne templates.txt me numra të radhës dhe pritet nga përdoruesi (user) të shtypë numrin e kallëpit të dëshirurar dhe ta konfirmojë këtë duke shtypur pullën "enter". Skripta nuk ka ndonjë mekanizëm për të validuar të dhënat hyrëse, por mund lehtë të modifikohet po u paraqit nevoja për këtë.

Nga ky moment skripta vazhdon ekzekutimin, krijon rezultatet, i ruan ato në një fajl të ri në nën-direktoriumin "results" dhe e hap atë fajl me editorin standard për fajlat e llojit ".txt". Mbetet vetëm një copy/paste.

Programim të këndshëm !

PS: Ju kërkoj falje për formatimin jo ideal të pjesëve të kodit. Editori i këtij blogu, m'i konverton automatikisht në rreshta shumë më të rrallë se origjinali.  



Comments