1.11.2023

Powershell fonksiyon oluşturma

Diğer programlama dillerinde olduğu gibi, Powershell'de de tekrarlanan kodları bir fonksiyon içinde tutup hızlıca erişime açabiliriz. Benim buradaki amacın bir fonksiyonun oluşturulmasıındaki birkaç anahtar noktayı vurgulamak.

Bir amacımız olduğunu varsayalım; bir genel (public) IP adresini hakkında coğrafi konum bilgisi alacağız. Bu amaçla bir fonksiyon oluşturuyoruz. Bu fonksiyonun adı Get-IPGeoInfo olsun. Powershell'de fonksiyonlara girişler (sorgu yapacağımız IP adresi) parametreler yardımıyla gönderilir. Örneğin terminalde

PS> Get-IPGeoInfo -IPAddress 195.175.39.39

gibi bir şekilde fonksiyonumuz çağrılabilir. Bunun için fonksiyon kodunun şu şekilde oluşturulması gerekir:

function Get-IPGeoInfo {
   param(
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Bu şekilde,  ilk satırda yazıldığı gibi bir giriş sağlanırsa cevap alınabilir.

Ama IP adresinin terminalden verilmediğini düşünelim. Bu durumda fonksiyon bir boş bir IP verisi için sorgu yapacağından hata verir. Bunun önüne geçmek için IPAddress parametresinin zorunlu sağlanmasını isteyebiliriz. Bu amaçla

function Get-IPGeoInfo {
   param(
      [Parameter(Mandatory=$true)]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

kırmızı ile vurguladığım eklemeleri yapabiliriz. Ayrıca her seferinde IPAddress değil de sadece IP yazarak parametresini belirtmek istiyorsak bu parametre tanımına bir alias ekleyebiliriz.

function Get-IPGeoInfo {
   param(
      [Parameter(Mandatory=$true)][alias("ip")]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Benzer şekilde fonksiyonumuz için de alias belirtebiliriz. Her seferinde Get-IPGeoInfo yerine kısaca ipinfo yazmak için param bloğunun hemen öncesine alias ekleyebiliriz.

function Get-IPGeoInfo {
   [Alias('ipinfo')]
   param(
      [Parameter(Mandatory=$true)][alias("ip")]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Fonksiyonumuzun Powershell'in ortak parametrelerine erişebilmesi için CmdletBinding anahtar kelimesini kullanabiliriz.

function Get-IPGeoInfo {
   [CmdletBinding()]
   [Alias('ipinfo')]
   param(
      [Parameter(Mandatory=$true)][alias("ip")]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Ayrıca CmdletBinding'in içini dolduracak ek parametreler için şu adrese bakılabilir.

Parametre tanımlamasında kullanılabilecek başka ayrıntılar da var. Örneğin yukarıdaki örneğimizde giriş parametresi sadece 1 tane olduğu için bu parametreyi her seferinde -ip diye kısa şekliyle bile belirtmeye gerek yok sadece (yeni alias'ımızı da hesaba katarak)

PS> ipinfo 195.175.39.39

şeklinde çalıştırabiliriz. Ama birden fazla parametremiz olsaydı hangisinin varsayılan olacağını (ve dolayısıyla diğer parametrelerin sıralarını) belirtmek için Position anahtar kelimesini kullanabiliriz. Bu şekilde her seferinde IP adresini elle yazmak yerine başka bir komutun çıktısını boru hattından (pipeline) almak için ValueFromPipeline'ı kullanabiliriz. Ayrıca Boolean veri tipindeki değerleri =$true şeklinde belirtmeye de gerek yok, sadece anahtar kelime ismini belirtmek yeterli, aşağıdaki gibi.

function Get-IPGeoInfo {
   [CmdletBinding()]
   [Alias('ipinfo')]
   param(
      [Parameter(ValueFromPipeline,Mandatory,Position=0)][alias("ip")]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Fonksiyonumuzun birden fazla parametresi varsa ve bu parametreleri isimlerini belirtmeden de çağırabilmek istiyorsak Position özelliğini kullanabiliriz. Örneğin param(.) bloğunda şu şekilde birşey olabilir:

param(
     [Parameter(Position=0)][string]$IPAddress,
     [Parameter(Position=1)][string]$HizmetSaglayici
)

Böylece artık fonksiyonumuzu

PS> ipinfo 1.1.1.1 ip-api.com

gibi kullandığımızda sırasıyla sorgu yapılacak IP adresini 1.1.1.1 olarak alacak, ve diğer parametreyi de hizmet sağlayıcı olarak kabul edecek. Elbette bu işlev şu anda fonksiyonumuzda yok, eklenmeli.

Bazı parametrelerin değerlerinin belli bir kümenin elemanlarından olmasını isteyebiliriz. Elle her seferinde yazmaktansa belli sınırlı sayıda değerden birini tab tuşu yardımıyla seçebiliriz. Bu amaçla ilgili paramterenin başına ValidateSet eklenebilir. Örneğin fonksiyonumuzda birden fazla GeoIP hizmet sağlayıcı varsa (ki şu andaki kodda bu yok) ve bunlardan birini seçmemiz gerekiyorsa:

function Get-IPGeoInfo {
   param(
      [string]$IPAddress,

      [ValidateSet("ipapi","geoipinfo","whereisip")][string]$service="ipapi"
   )
   ...

}

Hatta varsayılan olarak ipapi hizmetini de seçmiş olduk.

Çalışma zamanı istisnialarını yönetmek için bir try-catch bloğu kullanılabilir. Bunun için try içindeki cmdlet'in varsayılan hata durumundaki eylemi stop olarak ayarlanmalı ki catch bloğu işletilebilsin. Örneğin:

 

function Get-IPGeoInfo {
   param(
      [string]$IPAddress
   )

   try {

   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress" -ErrorAction stop
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

  }

  catch {

   Write-Error $_.Exception.Response.StatusCode

   Write-Error $_.ErrorDetails.Message

   $cikti = @{

      Sehir="-alinamadi-"

      Ulke="-alinamadi-"

      Operator="-alinamadi-"

   }

   finally {

   $cikti

   }
}

Bunun dışında boru hattından alınacak verinin işlenmesi için Begin-Process-End gibi fonksiyon bölümleri de koda eklenebilir.

Hiç yorum yok: