- Yazılım hızla gelişmiş olsa da işletim sisteminin ortam değişkeni sistemi hâlâ onlarca yıl önceki yapısını koruyor
- Ortam değişkenleri, namespace'i ve tipi olmayan genel bir string sözlüğü biçiminde, son derece basit bir yapıya sahip
- Linux'ta ortam değişkenleri
execve sistem çağrısı üzerinden ebeveyn süreçten çocuğa aktarılır
- Bash, glibc, Python gibi yapılar ortam değişkenlerini sırasıyla hash map, dizi ve sözlük sarmalayıcısı biçiminde yönetir
- POSIX standardı adlarda yalnızca büyük harf kullanımını şart koşmaz; pratikte küçük harfli adların kullanımı da önerilir ve kurallar esnektir
Ortam değişkeni nedir
- Programlama dilleri hızla gelişmiş olsa da işletim sisteminin sunduğu süreç çalıştırma temeli, özellikle ortam değişkenleri tarafında neredeyse hiç değişmedi
- Bir uygulama çalıştırılırken ayrı bir dosya ya da IPC kullanmadan çalışma zamanı parametreleri aktarmak gerektiğinde, gerçekte ortam değişkeni tabanlı arayüze başvurmak zorunda kalınıyor
- Ortam değişkenleri, namespace'i de tipi de olmayan, düz bir string sözlüğü işlevi görüyor
Ortam değişkenlerinin oluşturulması ve aktarım yapısı
- Ortam değişkenleri, süreçler arasında değer aktarmanın geleneksel yolu olup ebeveyn süreç çocuk süreci çalıştırırken birlikte iletilir
- Yani yapı, ebeveyn süreçten çocuk sürece miras alınması şeklindedir
- Linux'ta
execve sistem çağrısı yürütülebilir dosyayı, argümanları ve ortam değişkenleri dizisini (envp) parametre olarak alır
- Örnek çalıştırma komutu:
ls -lah ise
- filename:
/usr/bin/ls
- argv:
['ls', '-lah']
- envp:
['PATH=...','USER=...']
- Ebeveyn süreç, mevcut ortamı çocuğa olduğu gibi aktarabileceği gibi tamamen yeni bir ortam da kurabilir
- Neredeyse tüm araçlar (Bash, Python
subprocess.run, C kütüphanesi execl vb.) ortam değişkenlerini aynen aktarır
- İstisna olarak
login gibi bazı araçlar yeni bir ortam oluşturur
Ortam değişkenlerinin saklandığı yer ve iç işleme
- Çekirdek, program başlangıcında ortam değişkenlerini null-terminated string biçiminde stack üzerine yerleştirir
- Bu veriyi programın doğrudan değiştirmesi zordur; genelde program içinde kopyalanıp kendi yapısı içinde yönetilir
- Dillerin ve shell'lerin ortam değişkenlerini saklama biçimleri
- Bash: stack yapılı bir hash map (sözlük) ile yönetir
- Her fonksiyon çağrısında yerel scope için bir map oluşturulur
- Yalnızca
export edilmiş değişkenler çocuk sürece aktarılır
local ile tanımlanan değişkenler de export yoluyla çocuk sürece aktarılabilir
- Örnek:
export PATH ile yerel değişiklik çocuğa yansıtılır ama globale etki etmez
- glibc (C kütüphanesi):
putenv, getenv üzerinden environ adlı dinamik dizi yapısını yönetir
- Dizi yapısı nedeniyle hem sorgulama hem de değiştirme doğrusal zaman karmaşıklığına sahiptir
- Bu yüzden yüksek performans gerektiren veri saklama amaçları için uygun değildir
- Python: İçeride
os.environ ile sözlük gibi sunulur ama gerçekte C kütüphanesindeki environ dizisiyle bağlantılıdır
os.environ değeri değiştiğinde os.putenv çağrılır ve C kütüphanesine de yansır
- Ters yönde senkronizasyon olmadığı için tek yönlülük vardır
Ortam değişkenlerinin formatı ve izin verilen kapsam
- Linux çekirdeği ve glibc, ortam değişkeni formatı konusunda oldukça toleranslıdır
- Aynı adın yinelenmesiyle birden çok değer bulunabilir
= olmadan da kaydedilebilir ve emoji gibi özel karakterlere dair kısıtlama yoktur
- Kullanılabilir boyut sınırı
- Tek bir değişken: 128 KiB (genelde x64 ortamında)
- Toplam: 2 MiB (komut satırı argümanlarıyla paylaşılır)
- Ortam değişkenleri stack alanının 1/4'ünü geçemeyecek şekilde sınırlandırılmıştır
Ortam değişkenlerinin tuhaflıkları ve edge case'leri
- Bash, garip ortam değişkenleri (yinelenen adlar,
= içermeyen girdiler vb.) söz konusu olduğunda yinelenen adları kaldırır ve anormal girdileri yok sayar
- Değişken adında boşluk varsa Bash bu adı referanslayamaz, ama yine de çocuk sürece aktarabilir
- Örneğin Nushell, Python vb. araçlar boşluk içeren adlara sahip değişkenler oluşturabilir
- Bash bu tür girdileri ayrı bir hash map (
invalid_env) içinde tutup yönetir
Standart ortam değişkeni formatı ve adlandırma kuralları
- POSIX standardı, adda eşittir işareti (
=) bulunmadığı sürece bunu değişken olarak kabul eder
- Resmî öneri: ad yalnızca büyük harf, rakam ve alt çizgi içermeli (ilk karakter rakam olamaz)
- Küçük harfli değişkenler uygulamaya özel namespace için ayrılmıştır
- Standart araçlar yalnızca büyük harf kullanır, ancak küçük harfli değişken kullanımı da serbesttir
- Pratikte geliştiriciler çoğunlukla ALL_UPPERCASE biçiminde adlandırma yapar
- Önerilen kural: değişken adı için
^[A-Z_][A-Z0-9_]*$ regex'i, değer için UTF-8 kullanımı
- İstisna ya da uyumluluk kaygısı varsa POSIX Portable Character Set (ASCII) kullanılması önerilir
Sonuç
- Ortam değişkenleri hâlâ eski ama vazgeçilmez bir arayüz olarak işletim sistemi ile uygulamalar arasında sınır görevi görüyor
- Yapısal sınırlamalarına rağmen Bash, C, Python vb. bunları kendi yöntemleriyle sarmalayıp kullanmayı sürdürüyor
- Modern sistemlerde daha net namespace'lere ve tip sistemine sahip yapılandırma yönetimi yaklaşımlarına duyulan ihtiyaç giderek artıyor
2 yorum
İlk bakışta önemi azalıyormuş gibi görünse de Docker ve bulutun ortaya çıkışıyla yeniden kaçınılmaz hale geldi.
Hacker News yorumu
SRE/Sysadmin/DevOps/her neyse olarak çalışıyorum; blog yazısında ortam değişkenlerinin standartlaştırılması hakkında nispeten hafif şeylerden bahsedilmişti ama alternatif yaklaşımların da benzer şekilde can sıkıcı sorunlar yarattığını, özellikle de işin içine gizli bilgiler (
secrets) girdiğinde, belirtmek istiyorumUygulamanın Hashicorp Vault/OpenBao/Secrets Manager gibi belirli bir gizli bilgi deposuna (
vault) erişecek şekilde tasarlanması hızla ciddi bir vendor lock-in yaratıyor ve bu durum kütüphane düzeyine kadar yayıldığı için değiştirmesi çok zor oluyorVault'un erişilebilirliği kritik hale geliyor ve yükseltme ya da bakım gerektiğinde operasyon ekibi ciddi biçimde zor durumda kalıyor
Gizli bilgileri config dosyasıyla iletmek istediğinizde, bu sırları nasıl yerleştireceğiniz ayrı bir sorun oluyor; çünkü config dosyaları çoğu zaman herkese açık yollarda bulunuyor
Sonunda ya "ayrıcalıklı bir sistemin uygulamaya vermeden önce şablonu doldurmasına" ya da "tüm config dosyasını gizli bilgi deposunda tutup uygulamaya vermeye" dayanıyorsunuz
Şablon işi hataya çok açık ve tüm config dosyasını gizli bilgi deposuna taşıdığınızda da birinin yanlış dosya yükleme riski yüzünden ayrıca stres çıkıyor
Bugün çoğu sistem container üzerinde çalışıyor ama altyapıyı çok disiplinli yöneten bir şirket değilseniz config dosyaları sürekli alakasız yerlere konuyor; bu da mount sürecini daha kafa karıştırıcı hale getiriyor ve sık hata doğuruyor
JSON/YAML/TOML hangi formatı kullanırsanız kullanın kendine has bug'lar günlük hayatın parçası; örneğin YAML'ın Norway sorunu gibi
Gizli bilgilerin Kubernetes Secrets API üzerinden alındığını da gördüm ama bu da yine güçlü bir vendor bağımlılığı problemiyle geliyor
Özellikle operator benzeri sistemler tasarlamıyorsanız bu yaklaşımı çok önermem
Subprocess üzerinden ortam değişkeni ayarlarken ortaya çıkan sorunları da gördüm ama bugün ekiplerin bunun yerine daha dayanıklı olan ve bağımsız ölçeklenebilen message bus tabanlı sistemler kullandığını düşünüyorum
Bizim ekipte hafif, genel amaçlı bir Secrets kütüphanesi yapıp arka tarafta AWS Secrets Manager gibi vendor'a özgü backend'leri plugin şeklinde bağlayarak kullandığımız bir deneyim olmuştu
Yerel cache ayarı ve parametre bazında cache bypass seçenekleri vardı; böylece gerçek vendor'a bağımlı mantık sadece backend'de kalıyor, kütüphane ile uygulama ise vendor'dan bağımsız kalabiliyordu
Vault'a geçerken de sadece bir backend daha ekleyip ayarları değiştirdik ve sorunsuzca uyguladık
Kubernetes secret API'sinin neden vendor bağımlılığı (lock-in) problemi olarak görüldüğünü merak ediyorum
Acaba deployment yaml'ını Kubernetes deploy'u dışında başka bir amaçla kullanmaya çalıştığınız bir durum mu vardı?
Çoğu uygulamada secret'ı container'a mount edip ardından ortam değişkeni ya da json dosyası olarak uygulamaya verirseniz, ortamdan bağımsız şekilde okuyup yazabilirsiniz
Bildiğim kadarıyla etcd backend şifrelemesi de KMS ile yapılandırılabiliyor
Gizli bilgileri Kubernetes Secrets API üzerinden almanın neden lock-in olduğu tam olarak bana da mantıklı gelmiyor
Temelde K8s secrets şifrelenmiş olarak saklanmadığı için, bunun anlamlı olması adına (0) zaten K8s kullanıyor olmanız, (1) kontrol düzleminde şifrelemeyi etkinleştirmeniz ve (2) CSI driver gibi ek çözümler kullanmanız gerekiyor
Ayrıca Secret Store CSI Driver, Conjur dahil çeşitli backend'leri desteklediği için aslında lock-in'in tersine bir durum söz konusu
Bu yüzden config tarafında hâlâ env vars ve dotenv ağırlıklı gidiyorum
Ortam değişkeni tabanlı yapı çok basit ve secret manager gibi çeşitli araçlarla da çok uyumlu
Son birkaç yılda YAML tabanlı sOps'a da yavaş yavaş ilgi duymaya başladım
YAML, uygulama yapılandırmasını ifade etmek için gerçekten sezgisel ve sops ile yalnızca belli kısımları şifreleyerek yönetmek de kolay
GPG anahtar yönetimi biraz zahmetli olsa da bunu Vault ya da OpenBao gibi araçlarla çözmek mümkün
Ama tabii bu süreçte yine vendor lock-in konusu çıkıyor; gerçi OpenBao bu konuda biraz daha iyi gibi
Komut çalıştırma sonucuyla da ortam değişkeni alınabilir; böylece şablon süreci olmadan ve vendor lock-in yaşamadan çözmek mümkün olur
Bir başka ilginç nokta:
setenv()POSIX'te temelden bozuk, bu yüzden kütüphane kodunda asla kullanılmaması gerektiğini düşünüyorumUygulama kodunda kullanılması gerekse bile son çare olmalı ve mutlaka thread oluşturmadan önce kullanılmalı
getenv()ortam değişkeninin özgün işaretçisini doğrudan döndürdüğü için,setenv()değişkenin üstüne yazdığında hiçbir koruma mekanizması yokÇok dikkatli olmak gerekiyor
Ortam değişkenlerini düzgün ayarlamak için
execve()ile set etmek gerektiğini düşünüyorumBu yaklaşım yalnızca
exec()öncesi/sonrası ortam değişkeni üzerinden bilgi aktarılırken uygunKütüphane kodunda neden
setenvkullanmanız gereksin, bunu anlamıyorumSolaris bu sorunu çözdü ama Linux hâlâ aynı yaklaşımda ısrar ediyor
NetBSD'de uzun zamandır
getenv_r()diye güvenli bir alternatif var, FreeBSD de bunu yakın zamanda benimsedimacOS'un da muhtemelen yakında izleyeceğini düşünüyorum
Bunu glibc ya da POSIX'e ekleme girişimleri olmuştu ama reddedildi
Çeşitli platformlarda yaygınlaştıkça bir gün resmî olarak da kabul görmesini umuyorum
NetBSD getenv_r belgesi
FreeBSD commit'i
Ortam değişkenleri gizli bilgi taşımak için sık kullanılıyor ama bunun çok iyi bir pratik olduğunu düşünmüyorum
Linux'ta aynı kullanıcıyla çalışan tüm süreçler birbirlerinin ortam değişkenlerine bakabiliyor
Tehdit modeliniz ne olursa olsun, özellikle geliştirici makinelerinde aynı kullanıcı altında çalışan süreç sayısı çok fazla olduğu için bu endişe verici
Bu sorun, LLM agent'ları gibi container dışında birden fazla sürecin dolaştığı ortamlarda daha da ciddi hale geliyor
Ayrıca ortam değişkenleri genellikle alt süreçlere de aynen miras kalıyor; bu yüzden gizli bilgiye yalnızca tek bir sürecin ihtiyacı olsa bile gereksiz yere yayılma eğilimi oluşuyor
systemd, ortam değişkenlerini DBUS üzerinden tüm sistem istemcilerine gösteriyor ve resmî belgelerinde de ortam değişkenlerinde gizli bilgi tutulmaması uyarısı yer alıyorEğer bu gerçekten böyleyse, root'a özel bir unit için ayarlanmış ortam değişkenlerinin normal kullanıcılara da görünebileceği anlamına gelir ki bence pek çok sistem yöneticisi için oldukça şok edici olur
Sonuçta yalnızca secret manager'ın geçici dosya paylaşımı yoluyla secret aktardığı yapılar (ör. 1Password'in
opCLI'ı, flask, terraform vb.) hem ortam değişkeni hem düz metin dosya ifşasından kurtulabilen tek çözüm gibi görünüyorsystemd'nin credentials sistemi de böyle çalışıyor. Ama destek hâlâ çok sınırlıOrtam değişkenleri ya da düz metin dosyalar olmadan secret aktarmanın iyi bir yolunu bilen varsa paylaşmasını isterim
Örneğin 1Password
opclient tarafında, her oturum için benden ayrıca onay istediği için CLI oturumlarında bunu güvenli hissediyorum; kötü niyetli bir süreçopikilisini çağırsa bile yine ayrı onay istiyorŞimdi geriye kalan sorun, bu secret'ı gerçekten ona ihtiyaç duyan sürece nasıl aktaracağınız; yani sanki yine başladığımız yere dönüyoruz
systemdortam değişkenleri resmî dokümantasyon bağlantısı2012 civarından beri ortam değişkenleri normal bellek kadar güvenli hale geldi
İlgili commit kaydı
Başka bir sürecin ortam değişkenlerini okumak için artık mutlaka
ptraceyetkisi gerekiyor; zatenptraceile okuyabiliyorsanız gerçekte tüm gizli bilgileri de okuyabilirsiniz, dolayısıyla bunun ayrı bir endişe olması anlamsız deniyorKomut satırı bilgisi (
cmdline) farklı bir konu ama ortam değişkenleri artık bu yolla kolayca sızmıyorÇoğu işletim sisteminin güvenlik modelinde, bir kullanıcı olarak çalışmak o kullanıcının tüm yetkilerini bütünüyle devretmek anlamına gelir
FreeBSD'nin capsicum'u, Linux'un landlock'u, SELinux, AppArmor, Windows'un integrity label'ı gibi ek güvenlik katmanları istisna olabilir ama çoğunun sınırları belirgin
Sonuçta kendi süreçlerimi öldürmekte, durdurmakta ya da debug etmekte özgürüm;
ptrace/process_vm_readv/ReadProcessMemoryvb. yollarla bana ait süreçlerdeki secret'lara her zaman erişebilirimTamamen farklı güvenlik modelleri de var (kusursuz capability tabanlı işletim sistemleri gibi) ama büyük çoğunluk bu modeli izliyor; dolayısıyla bu sınırları ve sorumluluğu kabul etmek gerekiyor
Ortam değişkenleri ya da düz metin dosyalar kullanmadan secret aktarmanın iyi bir yolu olarak
memfd_secretaklıma geliyormemfd_secret man page
Dil ve framework desteği çok yaygın değil; özellikle Rust'ta ve belki Go'da da mümkünse FFI üzerinden denenebilir
PHP tarafında bunu sarmalayıp eklemeyi düşünmüştüm ama php-fpm'i de modifiye etmek istemediğim için yarım kaldı
Pratikte en güvenli yol, süreç yöneticisinin secret dosya tanımlayıcısını önceden açıp alt sürece aktarması ve bunun bellek vb. alanlara saçılmadan kullanılabilmesi olurdu
Klasik Unix güvenlik modeli, biraz iyileştirilmiş biçimleriyle hâlâ çok yaygın ama ucuz hesaplama ortamları ya da modern senaryolar için sınırları oldukça belirgin
Sırları başka süreçlerden gizlemeniz gerekiyorsa bunun doğru yolu en başta onları farklı kullanıcılar altında çalıştırmaktır
Ya da tamamen uzaktan erişim modeline geçebilirsiniz ama bunun da kendi dezavantajları ve karmaşıklığı var
Bugün container platformlarında config veya secret'ları ortam değişkeniyle vermek tavsiye edilen bir yaklaşım olma eğiliminde
Container içinde diğer süreçlerin ortam değişkenlerini görememesi üzerine tasarlanmış durumda
Ortam değişkenlerinin alt süreçlere miras kalması da bilinçli bir tasarım; çünkü secret değerini bilen ortam düzenleyicisi aynı ortamı doğrudan kuruyor
Endişe edilen sorunların çoğunu büyük problem olarak görmüyorum; istenirse bunu daha somut biçimde tartışabilirim
Birçok yorum secret yönetimi ve sorunları etrafında dönüyor ama ortam değişkenlerinin avantajlarını da düşünmekte fayda var
Ortam değişkenleri, Unix süreçlerini yapısal olarak birbirine bağlayan "süresiz kapsamlı, dinamik genişleyen değişken bağlama" mekanizmasıdır
Bunu yalnızca basit metin dosyalarıyla kıyaslamaktansa, ortam değişkenlerinin varlık nedeninin alt süreçlere güvenli bağlam aktarımı yapmak olduğunu hatırlamak gerekir
İç içe shell'ler ya da karmaşık programların alt süreçleri gibi, süreç yapısı ne kadar karmaşık olursa ortam değişkenlerinin rolü de o kadar değerli hale gelir
Varlock'u gerçekten faydalı bulduğum için tavsiye etmek istiyorum
Bir projede gereken ortam değişkenlerinin zorunlu/opsiyonel durumunu, veri tiplerini ve nereden alınacaklarını açıkça tanımlıyor; yönetimi de kolaylaştırıyor
Varlock resmî sitesi
Pratik deneyimden bir örnek vereyim: ortam değişkenlerinin ne kadar karmaşıklaşabileceğini eski şirketimde yaşadım; belirli bir
ENVdeğişkeninin nerede set edildiğini debug etmeye çalışırken tamamen kaybolmuştumBaşta bunun
.bashrcgibi tek bir yerde tanımlandığını sanıyordum ama aslında şirket geneli, bölge, iş birimi, takım, bireysel katman vb. en az 10 seviyelik bir zincir üzerinden ayarlanıyorduSonunda ancak bash debug bayrağını açınca bunun nerede set edildiğini adım adım izleyebildim
Başka dilleri de destekliyor mu bilmiyorum ama Node.js yakın zamanda ortam değişkeni erişimlerini ve değişikliklerini tam olarak izleyen bir komut satırı bayrağı ekledi
Node.js
--trace-envbelgesiDeğerler sayısız API üzerinden set edilebildiği ya da değiştirilebildiği için karmaşık debug senaryolarında çok faydalı görünüyor
İnsanın aklına "tek bir namespace yeterli olmaz mıydı" dedirten bir durum
Ben ortam değişkeni kullanımını çok uzun zaman önce bıraktım
Şimdi derleyicinin yanına bir
dmd.confdosyası koyuyor ve derleyicinin bunu doğrudan okumasını sağlıyorumOrtam değişkenlerinin en ciddi sorunu, örtük ve opak olmaları
*nixdünyasında çoğu uygulama ortam değişkenlerine dayanma eğilimindeDaha açık ve şeffaf yapılandırma yöntemleri (config dosyaları, remote servisler, komut satırı argümanları) ayrıca desteklense bile, bu alanda ortam değişkeni desteği bir gelenek haline gelmiş durumda
Sonuç olarak ortam değişkenleri de çocuk süreçler için klonlanıp genişletilen küresel bir hash map; 1979'da makul bir tasarım olabilir ama bugün çoğu zaman zararlı hale geliyor
Örneğin Kubernetes varsayılan olarak "service link" ortam değişkenleriyle container ortamını kirletiyor
Uygulamanın beklediği ortam değişkenleri ile varsayılan env var'lar çakıştığında, debug etmek son derece zorlaşıyor
Kubernetes resmî dokümantasyonu
Bunun dışında da
/bin,/usr/bin,/lib,/usr/libgibi eski kalıpları sorgulamadan sürdürme eğiliminin çok yaygın olduğunu düşünüyorumBkz: eski dizinlerin korunmasına dair Ubuntu Soru-Cevap
hjklgibi tuş kombinasyonları da bu tür köhne alışkanlıkların tipik örneği sayılabilirvi'daki
hjkl, 40 yıl önceki dumb terminal'lerden geliyor ve o terminal de çok az satmıştı(Nokia N9'dan bile daha az)
Linux'ta her ortam değişkeni ayarladığımda içime bir huzursuzluk çöküyor
Resmî olarak çalışan yöntem dağıtımdan dağıtıma biraz değişiyor ve internetteki yönergeleri izleseniz bile yeniden başlatınca ya da terminali kapatınca her şey kayboluyor
Keşke Windows'taki gibi küresel olarak kullanılabilen basit bir GUI ortam değişkeni düzenleyicisi olsa
Windows'ta değişikliklerin yansıması için terminali yeniden açmanız gerekiyor ama onun dışında hep düzgün çalışıyor
Ortam değişkenleri oturum değiştiğinde doğal olarak kalıcı olmaz; bu yüzden her oturumda yeniden çalışan bir yere (giriş/terminal vb.) yazmanız gerekir
Girişte
.bash_profile, alt oturumlarda ise.bashrcçalışır.bash_profileiçinden.bashrc'yisourceedip ayarların çoğunu.bashrc'de tutarsanız yönetmek kolaylaşırBash yerine zsh/fish gibi başka bir shell kullanıyorsanız ona göre düzenlemeniz gerekir
Linux'ta tüm terminaller için geçerli olan resmî ve birleşik bir ortam değişkeni GUI'si yok
Karmaşık bir parsing GUI'si yapılabilir ama düz metin düzenleyiciyle değiştirmek genelde daha kolaydır
Ağırlıklı olarak Linux kullanan biri olarak Windows'un bu davranışı bana daha rahatsız edici geliyor
Çok fazla uygulama ortam değişkenlerini kirletiyor; bir şey çalışmadığında sonunda
$SOFTWARE'ın tuhaf bir klasörden çalıştırıldığını fark etmek gibi karışıklıklar sık yaşanıyorsystemdkullanıyorsanız/etc/environmentya da/etc/environment.d/içineKEY=VALUEyazma yöntemi de varHatta bunun için bir GUI bile yapılabilir gibi
Ama ortam değişkenleri çalışan süreçlere sonradan enjekte edilemez; etkili olması için yeniden başlatmak gerekir, bu da doğal bir sınır
systemdresmî dokümantasyonuxkcd Standards karikatürü
Linux'ta ortam değişkeni ayarlamanın zaten 14 farklı rekabet eden yolu varken "haydi bunu birleştirelim" derseniz, ertesi gün 15. standart ortaya çıkar; bunu eğlenceli şekilde anlatıyor
En sevdiğim ortam değişkeni bilgisi şu: herkesin doğal olarak ortam değişkeni sandığı
PS1gibi değerler aslında ortam değişkeni değil, shell değişkenidirPS1'ienvkomutuyla da göremezsiniz