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.Argshiç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.