3.04.2025

Karakter kodlama (encoding)

Bu ilginç bir konu. Bilgisayarınızda bir metin düzenleyici açıp sadece "a" (tırnaklar olmadan) yazıp kaydettiğinizde diske ne yazılır? Yazılan hiçbir zaman sadece "a" olmaz. Aslında arka planda "a" diye birşey de yoktur. Bu konu 1960'larda ASCII standardı ile bir düzene oturtulmuş. Bir metin dosyasına yazılabilecek her şey bir tabloda toplanmış [1]. Bu standardın ilk geliştirildiği dönemde bu tabloda hangi harf ve diğer karakterlerin olacağına dair bazı görüşmeler olmuş. Bu görüşmelere Türkiye'nin de davet edildiği, ancak o dönemde Türkiye'nin başka işleri olduğuna dair bir şey duymuştum ama doğrulayamadım. Sonuçta bu tabloda ç ve ü gibi (Türkçe hariç diğer dillerde de var olan) harfler var, ama ı ve ğ gibi (sadece Türk alfabesine özgü) harfler yok. Türkiye 1980'lerin sonunda oyuna dahil olmuş ve ISO 8859-9 standardında Türkçe harflerin tümünün yer aldığı bir kodlama oluşturulmuş (bir de Windows 1254 mü var?).

Benzer şekilde çok sayıda karakter tablosu oluşmuş. Bir dosyanın düzgün görüntülenmesi için ilgili dosyanın belli bir karakter kodlaması / tablosu ile açılması gerek. Yani diskte 221 yazıyorsa her tabloda bu aynı harfe karşılık gelmeyebilir.

Bunların hepsi 1 karakterin 8 bitlik (1 byte) kodlama ile ifade edildiği dönemdi. Yani 2 üzeri 8 = 256 yapacağından, her tablo en fazla 256 karakter içeriyor ki buna bazı kontrol karakterleri, sayılar, küçük ve büyük harfler ile bazı özel sembollar (parantez, artı ve eksi işareti gibi) ve noktalama işaretleri de konduğunda aslında sadece 1 alfabe içerebilen bir tablodan bahsediyorum. Aynı anda sadece 1 karakter tablomuz olacağından iki farklı dilde harfin olduğu bir metin dosyasının var olması mümkün değildi.

Daha sonraları 1 byte (8 bitlik) kodlamanın yerine çok byte'lı kodlamanın kullanılması ve aslında dünya üzerindeki bütün alfabelerin sığdırılabileceği hatta bazı özel karakterlere bile yer kalacak bir tablonun yürürlüğe girmesi söz konusu olmuş. Bu durumda her harf iki veya daha fazla byte ile ifade edilir olmuş. İşler giderek karmaşıklaşıyor.

Standartların oluşması bana hep bu karikatürü çağrıştırıyor.

Sonunda açmak üzere olduğumuz dosyanın tek byte mı çift byte mı kodlama ile oluşturulduğunu önceden bilmemiz, hatta tek byte ise hangi karakter tablosu ile oluşturulduğunu bilmemiz/tahmin etmemiz/deneye yanıla bulmamız gerekiyor. Hala bazı metin dosyalarını açarken karakter kodlamasını söylememizi istemesi veya bazı (yüksek ihtimalle Türkçe) karakterlerin bozuk görüntülenmesi bundan kaynaklı.

Dosyada bazı karakterler (veya tümü) birden fazla byte kullanılarak ifade ediliyorsa bunu dosyanın başına bir imza koyarak işaretlemek istemişler. Bunun için Byte Order Mark (BOM) denen bir imza oluşturulmuş. Bir de bilgisayar dünyasında çok byte'lı verileri yazarken önce büyük basamakların mı yoksa küçük basamakların mı yazılacağı konusu var.

Dosyanın başındaki ilk 4 byte'ın aşağıdaki değerlerine göre dosya tipleri şöyle:

byte 1byte 2byte 3byte 4Anlamı
0x2b0x2f0x76-
UTF7
0xff0xfe
--Unicode - little endian
0xfe
0xff--Unicode - big endian
0x00
0x00
0xfe
0xffUTF32
0xef
0xbb
0xbf
-UTF8
----ASCII

Herhangi bir byte için "-" olması o byte'ın öneminin olmadığı anlamına geliyor. Koşulların hiçbirisi sağlanmıyorsa (en alttaki satır) dosyanın ASCII olduğu varsayılıyor.

UTF8 kodlaması için şu tablo, genişçe bir Unicode tablosu için şu tablo, her türlü kodlamayı tek sayfada görmek için de şu sayfa kullanılabilir.

Örnek birkaç karakter için kodlamalar

Karakter
ISO 8859-9
UTF8
U+
a
91 (0x61)
0x61U+0061
ç
231 (0xE7)  C3 A7
U+00E7
ğ240 (0xF0)
C4 9F
U+011F

---

[1] https://en.wikipedia.org/wiki/ASCII

[2] https://lokalise.com/blog/what-is-character-encoding-exploring-unicode-utf-8-ascii-and-more/

2.04.2025

Powershell ve emojiler

Promptu renklendirmek için emojiler işe yarayabilir. Windows Terminal'in de gelişiyle emoji desteği de zenginleşti.

Herhangi bir nedenle bir emoji buldum ve bunu script ile ekrana basmak istiyorum. Örneğin https://emojidb.org veya https://home.unicode.org adreslerinden ⚡ emojisini buldum. Bu emoji siteden kopyalandı ve panoda (clipboard) var. Bunu istediğim yere, Windows Terminal dahil, yapıştırabiliyorum. Ama elimde bunun bir kodu yok. Koduna nasıl ulaşabilirim?

[char]::ConvertToUtf32("⚡", 0).ToString("X")

komutu bana

26A1

döndü. Şimdi bu kodu kullanarak her an

[char]::ConvertFromUtf32(0x26A1)

ile ⚡ emojisini kullanabilirim. Veya U+1f91e igi U ile başlayan kodlara sahipsek (örneğin home.unicode.org sitesinde bu şekilde veriliyor) U+'dan sonra gelen kodu doğrudan kullanabiliriz.

[char]::ConvertFromUtf32(0x1f91e)

Vereceği sonuç

🤞

olur.

Değer 65536'dan küçükse sadece [char] ile görüntüleyebiliriz.

[char]36000
負 

Benzer şekilde 65536'dan küçük kodlara karşılık gelen unicode karakterler için şu daha basit bir şekilde kodları bulunabilir.

[int][char]'負'
36000 

Powershell core 7 ile bunun yerine

`u{26A1}

ile de aynı şeyi yapabiliyorum.

21.03.2025

The package provider requires 'PackageManagement' and 'Provider' tags hatası

Bir makinede powershell ile PowershellGallery.com'dan bir modül kurmak istediğimde

Install-Module -Name PSWritePDF

şöyle bir hata alıyordum:

PackageManagement\Install-PackageProvider : No match was found for the specified search criteria for the

provider 'NuGet'. The package provider requires 'PackageManagement' and 'Provider' tags. Please check if the

specified package has the tags.

At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:7468 char:21

+ ...     $null = PackageManagement\Install-PackageProvider -Name $script:N ...

+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (Microsoft.Power...PackageProvider:InstallPackageProvider) [I

   nstall-PackageProvider], Exception

    + FullyQualifiedErrorId : NoMatchFoundForProvider,Microsoft.PowerShell.PackageManagement.Cmdlets.Install

   PackageProvider


PackageManagement\Import-PackageProvider : No match was found for the specified search criteria and provider

name 'NuGet'. Try 'Get-PackageProvider -ListAvailable' to see if the provider exists on the system.

At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:7474 char:21

+ ...     $null = PackageManagement\Import-PackageProvider -Name $script:Nu ...

+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidData: (NuGet:String) [Import-PackageProvider], Exception

    + FullyQualifiedErrorId : NoMatchFoundForCriteria,Microsoft.PowerShell.PackageManagement.Cmdlets.ImportP

   ackageProvider


WARNING: Network connectivity may not be available, unable to reach remote sources.

WARNING: Unable to bootstrap the required package provider due to problems with network connectivity. Please

fix your network connection. If this is not possible, refer to 'Get-Help Install-PackageProvider' or

https:/go.microsoft.com/fwlink/?LinkId=626941 for guidance on installing the package provider manually.

WARNING: Unable to download from URI 'https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409' to ''.      WARNING: Unable to download the list of available providers. Check your internet connection.                  PackageManagement\Get-PackageProvider : Unable to find package provider 'NuGet'. It may not be imported yet.  Try 'Get-PackageProvider -ListAvailable'.                                                                     At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:7478 char:30                + ... tProvider = PackageManagement\Get-PackageProvider -Name $script:NuGet ...

+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : ObjectNotFound: (Microsoft.Power...PackageProvider:GetPackageProvider) [Get-Pa

   ckageProvider], Exception

    + FullyQualifiedErrorId : UnknownProviderFromActivatedList,Microsoft.PowerShell.PackageManagement.Cmdlet

   s.GetPackageProvider


Install-Module : NuGet provider is required to interact with NuGet-based repositories. Please ensure that

'2.8.5.201' or newer version of NuGet provider is installed.                                                  At line:1 char:1                                                                                              + Install-Module -Name PSWritePDF                                                                             + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                                                                 + CategoryInfo          : InvalidOperation: (:) [Install-Module], InvalidOperationException                   + FullyQualifiedErrorId : CouldNotInstallNuGetProvider,Install-Module

Çözüm olarak şu sitede gösterildiği gibi

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

ile TLS 1.2'yi Service Point Manager olarak ayarladıktan sonra asıl kurmak istediğim PSWritePDF'i kurmayı başardım.

19.03.2025

Timeout özelliği olan bir powershell ping fonksiyonu

Powershell'de Test-Connection cmlet'i ping yerine kullanılabilir. Ping'e benzer şekilde 4 adet ICMP paketi gönderir ve cevabını bekler. Bazen betiklerin içinde hedef cihazın canlı olup olmadığını anlamak için

Test-Connection -Computername hedef -Quiet -Count 1

gibi bir komut kullanırım, sadece True veya False dönen. Hedef cihaz açıksa ve ping'e cevap veriyorsa hemen True döner. Ama hedef cihaz kapalıysa False dönmesi uzun sürer. Powershell 5.1'de Test-Connection'ın bir timeout parametresi de yok. Onun için şöyle bir fonksiyon işimi görüyor:

function Test-ICMP {
    param (
        [string]$Computername = "8.8.8.8",
        [int]$Timeout = 1000  # Milisaniye
    )

    $ping = New-Object System.Net.NetworkInformation.Ping
    $task = $ping.SendPingAsync($Computername, $Timeout)

    $completed = [System.Threading.Tasks.Task]::WaitAny(@($task), $Timeout)

    if ($completed -eq 0) {
        # Ping işlemi tamamlandı, sonucu al
        $reply = $task.Result
        if ($reply.Status -eq "Success") {
            $True
        } else {
            $False
        }
    } else {
        # Timeout süresinden uzun sürdü
        $False
    }
}

Hiç bir parametre kullanmazsam varsayılan hedef olarak Google'ın birincil DNS sunucusu 8.8.8.8'e 1 adet ICMP request paketi gönderir ve 1000 ms bekler. Ama yerel ağdaki bir makinenin açık mı kapalı mı olduğunu anlamak için

Test-Connection -Computername hedef -Timeout 100

yazabilirim. 100 ms içinde açıksa True, kapalı (veya cevap dönmüyorsa) False cevabını alırım.

12.03.2025

Uzun powershell komutları tamamlanınca sesli uyarı versin

Powershell'de bazı komutların tamamlanması uzun sürebiliyor. Uzun sürsün, sorun değil. Ama bitince haberim olsa keşke diye düşünürken aklıma bir yöntem geldi.

Zaten profil dosyamın içinde en son komutun ne kadar sürdüğünü hesapladığım bir bölüm vardı.

    if ((Get-History).Length -gt 0)
    {
        $LastExecutionTime = [long]((Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime).TotalMilliSeconds
    }

Her komut tamamlandığında bir sesli uyarı almak da istemiyorum. Ne zaman almak isterim diye kendime sordum, aldığım cevap 5 saniye oldu. Bana da mantıklı geldi. Bu if bloğunu şöyle güncelledim:

    if ((Get-History).Length -gt 0)
    {
        $LastExecutionTime = [long]((Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime).TotalMilliSeconds
        if ($LastExecutionTime -gt 5000) {
            Play-Sound -Play "C:\Windows\Media\Windows Proximity Notification.wav"
        }
    }

Zaten Play-Sound diye bir fonksiyonum vardı. Yoksa onu da şu şekilde oluşturabilirdim:

function Play-Sound
{
    param
    (
        [string]$Play = "C:\Windows\Media\Alarm01.wav"
    
    $sound = New-Object System.Media.SoundPlayer;
    $sound.SoundLocation="$Play";
    $sound.Play()
}

Tabi bunların hepsi $profile dosyamın içindeki

  function profile {
  ...
  }

fonksiyonu içinde oluyor.

22.02.2025

bash prompt'a en son komut çalışma süresini eklemek

bash promptumda en son çalışan komutun ne kadar süre ile çalıştığını gösteren bir alan olmasını istiyorum. Bu işi doğrudan yapmanın bir yönteminin olmadığını öğrendim. Ama dolaylı olarak yapmanın yolları var. trap ve PROMPT_COMMAND gibi yönergeler işe yarıyor. Yazdığımız komutu çalıştırmadan hemen önce çalışacak fonkisyonu trap ile bildiriyoruz. Promptu oluşturacak fonksiyonu işe PROMPT_COMMAND yönergesi ile belirliyoruz. Örneğin preexec adında bir fonksiyon oluşturup içeriğini şöyle belirledim.

function preexec() {
    export TIMER_START=(date +%s%3N)
}

Bu fonksiyon, TIMER_START adında bir değişken oluşturup, içeriğini 01.01.1970'ten bu yana geçen milisaniyelere eşiytleyecek. Sonra da precmd adında bir fonksiyon oluşturup içeriğini aşağıdaki gibi belirledim.

function precmd() {
local TIMER_END=(date +%s%3N)
local TIME_DIFF=$((TIMER_END-TIMER_START))
if (( TIME_DIFF > 1000 )); then
local DURATION="$((TIME_DIFF / 1000)) s"
else
local DURATION="$TIME_DIFF ms"
fi
export PS1="($DURATION) $ "
}

Bundan sonra preexec'i trap ile, precmd'yi de PROMPT_COMMAND ile belirliyorum.

trap 'preexec' DEBUG 
PROMPT_COMMAND=precmd

Ancak bundan sonra farkettim ki preexec fonksiyonu birden fazla işliyor. Benim tespit ettiğim bir kez komutu çalıştırmadan önce, bir kez de komutu çalıştırdıktan sonra. Bu ilginç bir konu ama derinlerine dalmak istemedim. Basit bir şekilde bir komut için sadece 1 kez çalıştırılmasını bir değişkenle denetledim.

preexec_active=0
function preexec() {
    if [[ $preexec_active -eq 0 ]]; then
    export TIMER_START=(date +%s%3N)
    preexec_active=1
    fi
}

Benzer şekilde precmd fonksiyonunun içine de bu değişkeni eski haline döndürecek bir satır ekledim.

function precmd() {
    local TIMER_END=(date +%s%3N)
   local TIME_DIFF=$((TIMER_END-TIMER_START))
    if (( TIME_DIFF > 1000 )); then
        local DURATION="$TIME_DIFF s"
   else
        local DURATION="$TIME_DIFF ms"
   fi
   export PS1="($DURATION) $ "
    preexec_active=0
}

Bu şekilde herşey istediğim gibi oldu.

14.02.2025

Bir web sayfasının yüklenme süresini ölçmek

İlk defa bir web sayfasının yüklenme süresini ölçmem gerektiğinde şöyle birşey düşünmüştüm:

(1..5) | % {$a=(measure-command {iwr "http://www.ozmener.net"}).TotalMilliseconds; "$_ - $a"}

Daha sonra farkettim ki bu şekilde sadece varsayılan html, asp veya php sayfasını almaktan başka birşey yapmıyorum. Sayfadaki resimler, script'ler veya başka sitelerden alınan iframe'ler hesaba katılmıyor. Bunu powershell ile nasıl yapacağımı bilemedim. Her konuda bilgili arkadaşım ChatGPT bana

nmp install playwright

önerdi. Tabi bunun için önce nodejs'i kurmam gerek. Kurduktan sonra  karşılaştığım hata sonrası bir de

npx playwright install

komutunu verdikten sonra bir measure.js dosyasının içine

const { chromium } = require('playwright');
(async () => {
const url = process.argv[2] || 'https://example.com';

// Launch a headless browser with SSL verification disabled
const browser = await chromium.launch({
headless: true, // Optional: Set to false if you want to see the browser
});
const context = await browser.newContext({
ignoreHTTPSErrors: true // Ignore SSL certificate errors
});
const page = await context.newPage();

// Measure page load time
const start = Date.now();
await page.goto(url, { waitUntil: 'networkidle' });
const loadTime = Date.now() - start;

console.log(`Page loading time for ${url}: ${loadTime} ms`);

// Close the browser
await browser.close();
})();

yazıp komut satırından

node measure.js "http://www.ozmener.net"

yazınca

Page loading time for http://www.ozmener.net: 811 ms

sonucunu aldım. Bir siteye Chromium ile girmek ile Firefox ile girmek arasında bir fark vardır elbet, milisaniyeler seviyesinde. Bu ölçümün hassasiyeti farklı tarayıcıların sayfayı oluşturma performanslarını ölçecek hassasiyette değil. Safi sayfa öğelerinin uzaktan indirilmesi, önemsediğimiz.