İçeriğe geç
academia.sh

Ders 12 / 12

Komut Satırı Uygulamaları

Kursun ve konunun son dersi. Bir bayrak ayrıştırıcısına elle verilen dizgi dizisinden tam sayı, mantıksal, süre ve dizgi tiplerinin geri kurulması; tanınmayan bir bayrağın ayrıştırmayı durdurduğu; alt komut yapısında her alt komutun kendi bayrak kümesini taşıdığı koşturularak ölçülüyor. Sınırlayıcı ölçüm: ayrıştırıcı beklenen tipi geri kurur, değerin anlamını değil — negatif bir adet hiçbir hata üretmeden geçiyor. Kurs kapanışında on iki dersin ölçtüğü sınır geçişleri tek bir tabloda toparlanıyor.

İçindekiler

Önceki ders bir veritabanı satırının sürücünün dar tip kümesinden hedef değişkenin kendi Scan yöntemiyle geri kurulduğunu gösterdi ve konunun beş dersini kapattı: dosya ve arabellek, ikili dosyaya gömülen bir kaynak, metne dönen bir zaman değeri, alan adlarını taşıyan bir günlük kaydı, bir veritabanı satırı — beşinde de sınırı geçen değer kendi başına yeterli değildi, geri kurmayı sağlayan şey her seferinde karşı tarafın elindeki bir tarifti. Bu ders aynı soruyu programın kendi girişine taşıyor: bir komut satırı bayrağı da, tıpkı bir günlük satırı ya da bir veritabanı sütunu gibi, programa dizgi olarak giriyor. Kabuk Programlama kursu (M03/K02) bu argümanların kabuk tarafında nasıl kurulduğunu ve metnin nasıl işlendiğini zaten kurdu; bu ders onu tekrar etmiyor, yalnız argümanın programın eline geçtiği noktadan devam ediyor — dizgi elde tutulduğunda, tipi kim, hangi tarifle geri kuruyor?

Yanıt bu kursun standart kitaplığındaki komut satırı bayrağı ayrıştırıcısında duruyor. Bu ders boyunca yalnız kısaca “bayrak” deniyor, ama kastedilen hep bu: bir çağrının önüne - ile eklenen, dizgi olarak gelen bir komut satırı seçeneği. Ayrıştırıcı bir dizgi dizisini alıp, kaynakta önceden bildirilmiş bayrak adlarına göre, her bayrağın beklediği tipte bir değer üretiyor — tarif burada da karşı tarafın elinde, ama bu kez karşı taraf ağın ötesi ya da bir sürücü değil, kaynağın kendisinde kume.Int, kume.Bool, kume.String gibi çağrılarla yazılmış bildirimler. Bu ders üç şeyi ölçüyor: ayrıştırıcının dört tipi (tam sayı, mantıksal, süre, dizgi) dizgiden nasıl geri kurduğunu, tanınmayan bir bayrağın ayrıştırmayı nerede durdurduğunu, ve alt komut yapısının her alt komuta kendi bayrak kümesini nasıl verdiğini. Gerçek argüman listesi (os.Args) hiçbir yerde okunmuyor — bayrak kümesine her seferinde elle kurulmuş bir dizgi dizisi veriliyor, tıpkı önceki derste kukla sürücüye elle kurulmuş bir satır verildiği gibi. Konunun son ve kursun kapanış dersi olarak bu ders ayrıca on iki dersin ölçtüğü sınır geçişlerini tek bir tabloda topluyor.

Bayrak Ayrıştırıcının Dizgiden Geri Kurduğu Dört Tip

Standart kitaplığın bayrak paketi, tek bir küresel bayrak kümesi (flag.CommandLine) yanında, kendi bayrak kümesini kurmayı da destekliyor: flag.NewFlagSet bağımsız bir küme döndürüyor, ve bu küme os.Argse hiç dokunmadan, kendisine verilen herhangi bir dizgi dilimini ayrıştırabiliyor. Bu ders boyunca yalnız bu bağımsız kümeler kullanılıyor — bir kısıt değil, gerçek kaynak yasağının doğal bir sonucu: os.Args koşumun kendi ortamına bağlı bir kaynak, bu yüzden ölçüm ona hiç dokunmuyor.

SS20. — Dört bayrak (tam sayı, mantıksal, süre, dizgi) bir bayrak kümesine bildiriliyor; elle kurulmuş bir dizgi dizisi ayrıştırılıp her bayrağın geri kurduğu değerin tipi %T ile okunuyor.

// tipler.go — bayrak ayristiricinin dizgiden geri kurdugu dort tip
package main

import (
	"flag"
	"fmt"
	"io"
)

func main() {
	kume := flag.NewFlagSet("olcum", flag.ContinueOnError)
	kume.SetOutput(io.Discard)

	adet := kume.Int("adet", 0, "adet")
	kalici := kume.Bool("kalici", false, "kalici mi")
	bekleme := kume.Duration("bekleme", 0, "bekleme suresi")
	ad := kume.String("ad", "", "ad")

	girdi := []string{"-adet", "12", "-kalici", "-bekleme", "1h30m", "-ad", "vida"}
	kume.Parse(girdi)

	fmt.Println("-- dizgiden geri kurulan dort tip --")
	fmt.Printf("%-8s kaynak=%-7q deger=%-8v tip=%T\n", "adet", "12", *adet, *adet)
	fmt.Printf("%-8s kaynak=%-7q deger=%-8v tip=%T\n", "kalici", "(var)", *kalici, *kalici)
	fmt.Printf("%-8s kaynak=%-7q deger=%-8v tip=%T\n", "bekleme", "1h30m", *bekleme, *bekleme)
	fmt.Printf("%-8s kaynak=%-7q deger=%-8v tip=%T\n", "ad", "vida", *ad, *ad)
}
-- dizgiden geri kurulan dort tip --
adet     kaynak="12"    deger=12       tip=int
kalici   kaynak="(var)" deger=true     tip=bool
bekleme  kaynak="1h30m" deger=1h30m0s  tip=time.Duration
ad       kaynak="vida"  deger=vida     tip=string

Dört bayrağın dördü de kaynakta dizgi olarak duruyor — girdi dilimindeki her eleman bir string, başka bir şey değil. kume.Intin döndürdüğü *int işaretçisinin arkasındaki değer "12" dizgisinden ayrıştırılan bir tam sayı; kume.Bool bir bayrağın varlığını (değer yazılmadan yalnız adının geçmesini) true olarak okuyor; kume.Duration "1h30m" dizgisini time.Duration tipinde, saat ve dakikaya ayrıştırılmış bir değere çeviriyor; kume.String ise hiçbir dönüşüm yapmadan aynı dizgiyi geri veriyor. Dördü de kendi tipini %T ile doğruluyor — ayrıştırıcı burada tam olarak Veritabanı Erişimi dersindeki Scan yönteminin yaptığı şeyi yapıyor: kaynakta tip bilgisi taşımayan bir veriyi (orada sürücünün dar tip kümesi, burada çıplak dizgi), hedefte önceden bildirilmiş bir tarifle bir Go tipine geri kuruyor.

Bu geri kurma otomatik değil, bildirime bağlı. kume.Int("adet", 0, "adet") çağrısı ayrıştırıcıya “adet adlı bayrak bir tam sayı taşıyacak” diye önceden söylüyor; bu bildirim olmasaydı ayrıştırıcının "12" dizgisini bir tam sayı mı, bir dizgi mi okuyacağını bilecek hiçbir yolu yoktu. Tıpkı yapı etiketinin bir JSON alanının adını taşıması gibi, burada da tarif kaynağın kendisinde değil, ayrıştırıcıyı kuran kodun kendisinde duruyor.

Tanınmayan Bayrağın Ne Yaptığı

Bir bayrak kümesi yalnızca kendisine önceden bildirilen adları tanıyor. Dizgi dizisinde bildirilmemiş bir ad geçtiğinde ayrıştırıcı bunu sessizce yok saymıyor — ayrıştırmayı orada durduruyor ve bir hata döndürüyor.

SS21. — Bilinen bir bayraktan sonra tanınmayan bir bayrak, ondan sonra da bilinen ikinci bir bayrak geçiyor; ayrıştırma tanınmayan bayrakta durduğu için ikinci bilinen bayrağın hiç okunmadığı ölçülüyor.

// bilinmeyen.go — taninmayan bir bayrak ayristirmayi durduruyor, sonrasi hic okunmuyor
package main

import (
	"flag"
	"fmt"
	"io"
)

func main() {
	kume := flag.NewFlagSet("olcum", flag.ContinueOnError)
	kume.SetOutput(io.Discard)

	adet := kume.Int("adet", 0, "adet")
	ad := kume.String("ad", "baslangic", "ad")

	girdi := []string{"-adet", "5", "-bilinmeyen", "x", "-ad", "vida"}
	hata := kume.Parse(girdi)

	fmt.Println("-- taninmayan bayragin ne yaptigi --")
	fmt.Println("ayristirma hata dondurdu mu:", hata != nil)
	fmt.Println("adet (taninmayandan once okundu):", *adet)
	fmt.Println("ad (taninmayandan sonra, hic okunmadi):", *ad)
}
-- taninmayan bayragin ne yaptigi --
ayristirma hata dondurdu mu: true
adet (taninmayandan once okundu): 5
ad (taninmayandan sonra, hic okunmadi): baslangic

adet bayrağı -bilinmeyenden önce geçtiği için ayrıştırıcı onu sorunsuz okuyup 5e atıyor. -bilinmeyen bayrağına geldiğinde ise kumede bu adla bildirilmiş hiçbir bayrak olmadığını görüyor ve Parse hemen bir hatayla dönüyor — dizgi dizisinin geri kalanı (-ad vida) hiç işlenmiyor, ad kendi varsayılan değerinde ("baslangic") kalıyor. Bu, Go’nun kendi dağıtımının çoğu komutunda görülen davranış: ayrıştırma bir bütün olarak başarılı ya da baştan sona hiç tamamlanmamış olarak sonuçlanmıyor, kısmi kalıyor — durma noktasından önceki bayraklar okunmuş oluyor, sonrasındakiler hiç görülmüyor.

kume.SetOutput(io.Discard) çağrısı burada da aynı gerekçeyle duruyor: flag.ContinueOnError modunda ayrıştırıcı normalde hata metnini kendi yazdığı bir akışa basıyor, ve bu metin hangi bayrağın tanınmadığını içeriyor — sürüme bağlı bir biçimde. Ölçülen tek şey hata != nil sonucu; hata metninin kendisi hiçbir yere yazılmıyor.

Alt Komut Yapısı: Her Alt Komutun Kendi Bayrak Kümesi

Tek bir bayrak kümesi tek bir düz komut için yeterli, ama birden çok alt işlevi olan bir araç genelde alt komut yapısını kullanıyor — ilk argüman bir işlevin adı, geri kalanı o işleve özgü bayraklar. Bu yapının kendisi dilin bir özelliği değil, flag.NewFlagSetin bağımsız kümeler döndürebilmesinin doğal bir sonucu: her alt komuda kendi kümesi veriliyor, ve bir kümede bildirilen bir bayrak ötekinde hiç var olmuyor.

SS22. — İki alt komut (ekle, listele) kendi bayrak kümesiyle çağrılıyor; sonra eklenin kendi bayrağı (-adet) listelenin kümesine verilip tanınmadığı ölçülüyor.

// altkomut.go — her alt komut kendi bayrak kumesini tasiyor, biri otekinde taninmiyor
package main

import (
	"flag"
	"fmt"
	"io"
)

func main() {
	ekleKumesi := flag.NewFlagSet("ekle", flag.ContinueOnError)
	ekleKumesi.SetOutput(io.Discard)
	ekleAd := ekleKumesi.String("ad", "", "urun adi")
	ekleAdet := ekleKumesi.Int("adet", 1, "adet")

	listeleKumesi := flag.NewFlagSet("listele", flag.ContinueOnError)
	listeleKumesi.SetOutput(io.Discard)
	listeleFiltre := listeleKumesi.String("filtre", "", "filtre")

	komutlar := map[string]*flag.FlagSet{"ekle": ekleKumesi, "listele": listeleKumesi}

	girdi1 := []string{"ekle", "-ad", "vida", "-adet", "20"}
	komutlar[girdi1[0]].Parse(girdi1[1:])
	fmt.Println("-- alt komut: ekle, kendi bayrak kumesi --")
	fmt.Printf("ad=%s adet=%d\n", *ekleAd, *ekleAdet)

	girdi2 := []string{"listele", "-filtre", "aktif"}
	komutlar[girdi2[0]].Parse(girdi2[1:])
	fmt.Println("-- alt komut: listele, kendi bayrak kumesi --")
	fmt.Printf("filtre=%s\n", *listeleFiltre)

	girdi3 := []string{"listele", "-adet", "5"}
	hata3 := komutlar[girdi3[0]].Parse(girdi3[1:])
	fmt.Println("-- listele kendi kumesinde 'adet' bayragini hic tanimiyor --")
	fmt.Println("hata verdi mi:", hata3 != nil)
}
-- alt komut: ekle, kendi bayrak kumesi --
ad=vida adet=20
-- alt komut: listele, kendi bayrak kumesi --
filtre=aktif
-- listele kendi kumesinde 'adet' bayragini hic tanimiyor --
hata verdi mi: true

Alt komut adının kendisi ("ekle", "listele") hiçbir bayrak kümesinin parçası değil — elle kurulmuş dizginin ilk elemanı, ayrıştırıcıya hiç verilmeden, çağrılacak kümeyi seçmek için kullanılıyor; ayrıştırıcının gördüğü yalnızca dizginin geri kalanı (girdi1[1:]). ekle kümesi -ad ve -adet bayraklarını tanıyor, listele kümesi yalnız -filtreyi. Üçüncü çağrı bu ayrımı doğruluyor: -adet bayrağı eklenin kümesinde geçerli bir bayrak, ama listelenin kümesine verildiğinde ayrıştırıcı onu hiç tanımıyor ve bilinmeyen bölümündeki aynı hatayı döndürüyor. İki küme aynı pakette, aynı main fonksiyonunda tanımlanmış olsa da birbirinden tamamen bağımsız — biri ötekinin bildirdiği hiçbir bayrağı miras almıyor.

Sınırlayıcı Ölçüm: Tip Geri Kurulur, Anlam Geri Kurulmaz

Önceki üç ölçüm ayrıştırıcının bir dizgiden beklediği tipi doğru geri kurduğunu gösterdi. Ama “beklenen tip” ile “doğru değer” aynı şey değil: ayrıştırıcı bir dizginin sözdizimsel olarak o tipe uyup uymadığına bakıyor, değerin anlamlı olup olmadığına hiç bakmıyor.

SS23.adet bayrağına negatif bir dizgi ("-3") veriliyor; ayrıştırıcı bunu geçerli bir tam sayı olarak kabul edip hiçbir hata döndürmüyor.

// anlam.go — ayristirici beklenen tipi geri kurur, degerin anlamini degil
package main

import (
	"flag"
	"fmt"
	"io"
)

func main() {
	kume := flag.NewFlagSet("olcum", flag.ContinueOnError)
	kume.SetOutput(io.Discard)
	adet := kume.Int("adet", 0, "siparis adedi")

	girdi := []string{"-adet", "-3"}
	hata := kume.Parse(girdi)

	fmt.Println("-- ayristirici beklenen tipi geri kurar, degerin anlamini degil --")
	fmt.Println("ayristirma hata verdi mi:", hata != nil)
	fmt.Printf("adet=%d tip=%T\n", *adet, *adet)
}
-- ayristirici beklenen tipi geri kurar, degerin anlamini degil --
ayristirma hata verdi mi: false
adet=-3 tip=int

-3 dizgisi bir tam sayının sözdizimine tastamam uyuyor — işaret, ardından rakamlar — bu yüzden kume.Intin ayrıştırma mantığı onu sorunsuz kabul ediyor ve adet değişkeni -3 değerini, int tipinde, hiçbir hata olmadan alıyor. Ama bir sipariş adedinin negatif olması alanın anlamı açısından geçersiz; bu geçersizliği ayrıştırıcı hiç bilmiyor, çünkü ayrıştırıcının tek işi dizgiyi bildirilen tipe çevirmek, o tipin değer aralığını sınırlı tutan bir kural uygulamak değil. Aynı durum önceki derslerde de görüldü: yapı etiketi bir alanın adını taşıyordu ama değerin geçerliliğini denetlemiyordu, sürücünün taraması bir sütunun tipini geri kuruyordu ama içindeki sayının anlamlı olup olmadığını sormuyordu. Bu ders o örüntüyü kapatıyor — geçerli bir aralık denetimi isteniyorsa, bu denetim ayrıştırıcının işi değil, ayrıştırılan değeri kullanan kodun kendi işi.

Kurs Kapanışı

On iki ders boyunca bu kursun tek sorusu şuydu: bir değer sınırı geçerken tip bilgisine ne oluyor? Ortak tanımın üç iddiası bu soruya üç yanıt verdi. Birincisi: sınırı geçen tip bilgisi kural olarak kayboluyor — ortak ölçümde on üç geçişin yedisinde karşı tarafa yalnız bayt ya da metin ulaştı, programın içindeki bir tip sınırın ötesinde bir sayı dizisine ya da bir satıra indirgendi. İkincisi: kaybolan bilgi geri kurulabiliyor, ama bunu yapan şey her seferinde tarifi tutan taraf oldu — yapı etiketi, hedef tipin kendisi, ayrıştırıcının beklediği tip, taramanın indirgediği tip; ortak ölçümde beş geçiş bu yolla yeniden kuruldu. Üçüncüsü, bu kursa özgü olanı: tip bilgisinin korunduğu tek geçiş, iki tarafın aynı tarifi paylaştığı geçişti — Sözleşme Temelli Servis İletişimi dersi bunu on üç geçişin yalnız birinde gösterdi.

On iki dersin kendi tablosu, ortak ölçümün on üç geçişiyle birebir aynı çözünürlükte değil: JSON Dizileştirme dersi tek başına dört alt geçiş ölçtü, bu ders de dört tipi tek derste ölçtü. Yön yine de aynı — on iki satırın beşi kayboluyor, altısı yeniden kuruluyor, biri korunuyor sınıfına düştü; çoğunluk hâlâ kayıp ya da tarifle geri kurma tarafında, koruma yine tek bir satırda kaldı. Sınırlayıcı okuma her derste bir biçimde tekrarlandı: “geri kuruldu” doğru kuruldu, ya da anlamlı bir değer geldi, demek değil. Bir sürücünün taşıdığı tip, bir yapı etiketinin taşıdığı alan adı, bir bayrak ayrıştırıcının geri kurduğu tam sayı — hepsi doğru tipi verdi, hiçbiri kaynağın niyet ettiği anlamı garanti etmedi.

Kurs aşırı bir genelleme yapmadan kapanıyor. Tip sistemi yalnız sınırın bu tarafında çalışıyor; sınırın öbür tarafında — ağda, diskte, bir sürücüde, bir dizgide — tip diye bir şey yok, yalnız bayt ve metin var. Geri kurma tarifi her zaman tutan tarafın işi, ve o tarif yanlış ya da eksik olduğunda hiçbir uyarı gelmiyor: alıcıda karşılığı olmayan bir alan sessizce düşüyor, yanlış bir hedefe tarama çalışma zamanında hata veriyor, negatif bir adet hiç sorgulanmadan geçiyor. Koruma, en güçlü sonuç, yalnız iki tarafın aynı tarifi paylaşmasına bağlı — ve bu paylaşımın kendisi kursun hiçbir dersinde otomatik değildi, hep ayrıca kurulması gereken bir şeydi.

Ders Ölçülen geçiş Sonucu Sınırlayıcı ölçüm
HTTP Sunucusu İsteğin yolu, üstbilgisi ve gövdesinin tip bilgisi taşıyıp taşımadığı kayboluyor İçerik tipi üstbilgisi bir ipucu, tip değil — yanlış ipucu sunucuda hiçbir hata üretmiyor
HTTP İstemcisi Yanıt gövdesinin istemci tarafında okunması ve kapatılması kayboluyor Gövde bir okuyucu, bir değer değil — ikinci okuma hatasız boş dönüyor
JSON Dizileştirme Yapı etiketiyle işaretli alanların JSON gidiş-dönüşü yeniden kuruluyor “Geri kuruldu” doğru kuruldu demek değil — karşılıksız alan sessizce düşüyor
Şablonlar Aynı yapının iki şablondan geçirilmiş çıktısı kayboluyor Alan adı çalışma zamanında aranıyor — olmayan alan hatası çıktının ortasında doğuyor
Sözleşme Temelli Servis İletişimi İki sürecin paylaştığı tarifle üretilen kaynağın alanları korunuyor Koruma tarifin dağıtımına bağlı — tarif ayrışırsa geçiş sessizce kayboluyora düşüyor
Gerçek Zamanlı İletişim Uzun süreli bağlantıda itilen mesajın gövdesi kayboluyor Çerçeveleme mesajın nerede bittiğini veriyor, içindeki değerin tipini vermiyor
Dosya ve Girdi/Çıktı Arayüzleri Dört kaynağın (dosya, arabellek, dizgi, ağ gövdesi) aynı okuyucudan geçmesi kayboluyor Kısmi okuma ile dosya sonu ayrı adımlar — ayrımı yönetmek çağıranın işi
Gömülü Kaynaklar Kaynak dosyanın üç Go tipine derleme zamanında bağlanması yeniden kuruluyor İkili, kaynak dosya silindikten sonra da aynı içeriği üretiyor — bağ derleme zamanında kuruldu
Zaman İşlemleri Bir anın metne dönüp geri gelmesi yeniden kuruluyor Ofset yeniden kuruluyor ama bölge adı taşınmıyor — Equal eşit, == ve String() farklı
Yapılandırılmış Günlükleme Aynı olayın düz metin ve anahtar-değer biçiminde yazılması yeniden kuruluyor Alan adı korunuyor, tip korunmuyor — geri kurma okuyanın kendi tarifine bağlı
Veritabanı Erişimi Sürücüden gelen beş sütunun hedef tipe taranması yeniden kuruluyor Tarama hedefin tipine bakıyor, kaynağın tipine değil — uyumsuz hedef çalışma zamanında hata veriyor
Komut Satırı Uygulamaları Bayrak kümesine verilen dizginin dört tipe ayrıştırılması yeniden kuruluyor Ayrıştırıcı beklenen tipi geri kuruyor, değerin anlamını değil — negatif adet hatasız geçiyor

Özet

  • Bayrak ayrıştırıcısı, elle kurulmuş bir dizgi dizisinden dört tipi (tam sayı, mantıksal, süre, dizgi) önceden bildirilen bayrak tanımlarına göre geri kurdu; os.Args hiçbir yerde okunmadı.
  • Tanınmayan bir bayrak ayrıştırmayı olduğu noktada durdurdu: durma noktasından önceki bayraklar okundu, sonrasındakiler hiç işlenmedi.
  • Alt komut yapısında her alt komut kendi bağımsız bayrak kümesini taşıdı; bir kümede bildirilen bir bayrak ötekinde hiç tanınmadı.
  • Sınırlayıcı ölçüm: ayrıştırıcı bir dizginin beklenen tipe sözdizimsel olarak uyup uymadığına baktı, değerin anlamlı olup olmadığına bakmadı — negatif bir adet hatasız geçti.
  • Kurs on iki ders boyunca aynı soruyu on iki ayrı sınırda sordu ve üç sonuçtan birine yazdı; çoğunluk kayboluyor ya da yeniden kuruluyor sınıfında kaldı, koruma yalnız iki tarafın aynı tarifi paylaştığı tek bir geçişte görüldü.
  • Kurs on iki dersin ölçtüğü yanıtları tek bir tabloda topladı; her satır kendi kaynak dersinden okundu.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat