> Proeseslerin Çevre Değişkenleri: İşletim sistemlerinin çoğunda var olan bir konudur. Prosese özgü "anahtar-değer" çiftlerini tutan sözlüksel bir veri yapısıdır. "anahtar" verildiğinde "değer" döndürmektedir. "hash-table", "dictionary" vb. veri yapılarının işletim sistemi tarafından organiza edilmiş halidir. Burada "anahtar" kısmına "Çevre Değişkeni", "anahtar" a karşı gelen "değer" için ise "Çevre Değişkeninin Değeri" denmektedir. Fakat unutmamalıyız ki "anahtar-değer" çifti bir yazı biçiminde OLMAK ZORUNDADIR. Örneğin, "anahtar" a karşılık gelen şey "ankara" yazısı olabilir, buna karşılık gelen "değer" ise "06" yazısı olabilir. Proseslerin çevre değişkenleri ve bunlara karşılık gelen değerleri pek çok işletim sisteminde, ilgili prosesin bellek alanı içerisindeki özel bit bölgede tutulmaktadır. Pekiyi bizler ne tür işlemlere haiziz? >> Bir çevre değişkeni, yani "anahtar", verildiğinde ona karşılık gelen "değer" i elde etmek için standart bir C fonksiyonu vardır. O fonksiyona "anahtar" bilgisini geçtiğimiz vakit, bizlere ona karşılık gelen "değer" i döndürmektedir. Prototipi aşağıdaki gibidir: #include char *getenv(const char *name); Fonksiyona geçilen adres '\0' karakteri ile biten bir adres olmalıdır. Ek olarak, bize geri döndürdüğü adrestekileri DEĞİŞTİRMEK TANIMSIZ DAVRANIŞTIR. Sadece "get" etmek için kullanmalıyız. "set" için başka fonksiyonlara ihtiyacımız vardır. Ek olarak bu adresi "free()" fonksiyonuna göndermemeliyiz. Statik ömürlüdür. Eğer "anahtar" a karşılık gelen bir "değer" yok ise iş bu fonksiyon NULL ile geri dönmektedir. Başarısızlık durumunda "errno" herhangi bir değere çekilmez. Proseslerin çevre değişkenleri UNIX/Linux sistemlerinde "Case-Sensitive", fakat Windows sistemlerinde böyle bir duyarlılık yoktur. Ek olarak çevre değişkenleri, yani "anahtar" lar, boşluk karakteri içermemektedir. Proseslerin çevre değişkenleri, ilgili prosesin bellek alanında olduğunu söylemiştik. "fork" işlemi sırasında da proseslerin bellek alanları da çoğaltıldığı için, çevre değişkenleri de çoğaltılmaktadır. Buradan hareketle diyebiliriz ki alt prosesin çevre değişkenleri de üst prosesten gelmektedir. Örneğin, "shell" programı üzerinden başka bir program çalıştırdığımız zaman, "shell" in çevre değişkenleri o programa aktarılmaktadır. "shell" programının çevre değişkenlerini "env" isimli komut üzerinden öğrenebiliriz. Bu komutu çalıştırdığımız zaman görüyoruz ki "anahtar-değer" çiftleri her satırda bir tanesi olacak şekilde ekrana basılmaktadır. Pekiyi "shell" programı çevre değişkenlerini nereden almaktadır? "shell" programı "login" programı tarafından çalıştırıldığı için, "login" programını da "terminal" çalıştırmaktadır. Zincirin halkaları biçiminde bir çağrım var. Bu da demekdir ki her bir program, çevre değişken listesine eklemeler yapmaktadır. Kümülatif bir yaklaşım söz konusudur. "shell" programının çevre değişkenler listesine eklediklerini şu linkten görebiliriz; https://man7.org/linux/man-pages/man1/sh.1p.html "shell" programı üzerinden "cd" komutunu çalıştırdığımız zaman, bu komut ile birlikte geçilen dizin, artık "shell" programının "PWD" isimli "anahtar" ına karşılık gelecektir. Fakat "chdir" isimli POSIX fonksiyonu böyle bir etkiye sahip değildir. "shell" programı üzerinden "anahtar" karşılık gelen "değer" i bulmak için "echo $HOME" şeklinde bir yaklaşım sergilemeliyizi. Bu durumda ekrana "HOME" a karşılık gelen basılacaktır. Windows sistemler için "echo %HOME%" şeklinde yapmalıyız. * Örnek 1, #include #include #include int main(int argc, char* argv[]) { /* # Command Line Arguments: # PATH */ /* # OUTPUT # /opt/swift/swift-5.7.3-RELEASE-ubuntu22.04/usr/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin */ if(argc != 2) { fprintf(stderr, "wrong number of arguments!...\n"); exit(EXIT_FAILURE); } char* value; if((value = getenv(argv[1])) == NULL) { fprintf(stderr, "environment variable not found!...\n"); exit(EXIT_FAILURE); } puts(value); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2, #include #include #include int main(int argc, char* argv[]) { /* # Command Line Arguments: # PatH */ /* # OUTPUT # environment variable not found!... */ if(argc != 2) { fprintf(stderr, "wrong number of arguments!...\n"); exit(EXIT_FAILURE); } char* value; if((value = getenv(argv[1])) == NULL) { fprintf(stderr, "environment variable not found!...\n"); exit(EXIT_FAILURE); } puts(value); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 3, #include #include #include int main(int argc, char* argv[]) { /* # Command Line Arguments: # PWD */ /* # OUTPUT # /home */ chdir("/home"); if(argc != 2) { fprintf(stderr, "wrong number of arguments!...\n"); exit(EXIT_FAILURE); } char* value; if((value = getenv(argv[1])) == NULL) { fprintf(stderr, "environment variable not found!...\n"); exit(EXIT_FAILURE); } puts(value); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } >> Prosesin "anahtar-değer" çift listesine yeni bir "anahtar-değer" çifti eklemek. >> Prosese ait olan bütün çevre değişken listesini komple elde etmek. >> Prosesin "anahtar-değer" çift listesine yeni bir "anahtar-değer" çifti eklemek için POSIX fonksiyonu olan "setenv" ve "putenv" isimli fonksiyonları kullanacağız. C standartlarında bir prosesin çevre değişkenlerine ekleme yapan bir fonksiyon bulunmamaktadır. >>> "setenv" fonksiyonu, aşağıdaki prototipe sahiptir: #include int setenv(const char *key, const char *value, int overwrite); Fonksiyonun birinci parametresi "anahtar" a karşılık gelirken, ikinci parametresi ise "değer" e karşılık gelmektedir. Üçüncü parametre ise "anahtar" ın halihazırda bulunması durumunda, ona karşılık gelen "değer" in değişsin mi değişmesin mi bilgisidir. Üçüncü parametre "non-zero" bir değer geçilirse, "değer" bilgisi değiştirilecektir. İş bu fonksiyon başarısızlık durumunda "-1", başarı durumunda "0" ile geri döner. Başarısızlık durumunda yine "errno" değiştirilir. * Örnek 1, Aşağıdaki fonksiyonda "anahtar=değer" biçiminde girilen girişler, '=' karakterinden itibaren ayırmıştır. #include #include #include #include void set_env_var(int argc, char* argv[]); void get_env_var(int argc, char* argv[]); int main(int argc, char* argv[]) { /* # Command Line Arguments: # ali=100 veli=200 selami=300 */ /* # OUTPUT # environment variable not found: ali=100 environment variable not found: veli=200 environment variable not found: selami=300 ali ---> 100 veli ---> 200 selami ---> 300 */ if(argc == 1) { fprintf(stderr, "too few arguments!...\n"); exit(EXIT_FAILURE); } get_env_var(argc, argv); set_env_var(argc, argv); get_env_var(argc, argv); return 0; } void set_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { /* * "argv[i]" yazısı içerisinde '=' karakteri arıyoruz. * Döngünün ilk turunda "ali=100" ele alındığı varsayılırsa, * "str" değişkeni '=' karakterini gösterecektir. */ if((str = strchr(argv[i], '=')) == NULL) { fprintf(stderr, "invalid argument: %s\n", argv[i]); continue; } /* * Eğer bu karakter bulunmuşsa, programın akışı buraya * gelecektir. '=' karakterini '\0' karakteri ile * değiştiriyoruz. Artık "ali=100" şeklindeki girişimiz * "ali\0100" halini aldı. Girişler bir yazı biçiminde * olduğu için aslında giriş "ali\0100\0" biçimindedir. */ *str = '\0'; /* * "str" değişkeni artık ilk '\0' karakterini, "str + 1" ise * '\0' karakterinin bir yanındaki karakteri gösteriyor. Yani * "1" karakterini. Dolayısıyla o nokta, başlangıç noktası * olarak ele alındı. Bitiş noktası olarak zaten yazının * sonundaki '\0' ele alınmaktadır. */ if(setenv(argv[i], str + 1, 1) == -1) { perror("setenv"); } } } void get_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { if((str = getenv(argv[i])) == NULL) { fprintf(stderr, "environment variable not found: %s\n", argv[i]); continue; } printf("%s ---> %s\n", argv[i], str); } } * Örnek 2, #include #include #include #include void set_env_var(int argc, char* argv[]); void get_env_var(int argc, char* argv[]); int main(int argc, char* argv[]) { /* # Command Line Arguments: # aliveli selami=300 */ /* # OUTPUT # environment variable not found: aliveli environment variable not found: selami=300 invalid argument: aliveli environment variable not found: aliveli selami ---> 300 */ if(argc == 1) { fprintf(stderr, "too few arguments!...\n"); exit(EXIT_FAILURE); } get_env_var(argc, argv); set_env_var(argc, argv); get_env_var(argc, argv); return 0; } void set_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { /* * "argv[i]" yazısı içerisinde '=' karakteri arıyoruz. */ if((str = strchr(argv[i], '=')) == NULL) { fprintf(stderr, "invalid argument: %s\n", argv[i]); continue; } /* * Eğer bu karakter bulunmuşsa, programın akışı buraya * gelecektir. '=' karakterini '\0' karakteri ile * değiştiriyoruz. */ *str = '\0'; /* * "str" değişkeni artık '\0' karakterini, "str + 1" ise * '\0' karakterinin bir yanındaki karakteri gösteriyor. * Dolayısıyla o nokta, başlangıç noktası olarak ele alındı. */ if(setenv(argv[i], str + 1, 1) == -1) { perror("setenv"); } } } void get_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { if((str = getenv(argv[i])) == NULL) { fprintf(stderr, "environment variable not found: %s\n", argv[i]); continue; } printf("%s ---> %s\n", argv[i], str); } } * Örnek 3, #include #include #include #include void set_env_var(int argc, char* argv[]); void get_env_var(int argc, char* argv[]); int main(int argc, char* argv[]) { /* # Command Line Arguments: # ali=100 ali=200 */ /* # OUTPUT # environment variable not found: ali=100 environment variable not found: ali=200 ali ---> 200 ali ---> 200 */ if(argc == 1) { fprintf(stderr, "too few arguments!...\n"); exit(EXIT_FAILURE); } get_env_var(argc, argv); set_env_var(argc, argv); get_env_var(argc, argv); return 0; } void set_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { /* * "argv[i]" yazısı içerisinde '=' karakteri arıyoruz. */ if((str = strchr(argv[i], '=')) == NULL) { fprintf(stderr, "invalid argument: %s\n", argv[i]); continue; } /* * Eğer bu karakter bulunmuşsa, programın akışı buraya * gelecektir. '=' karakterini '\0' karakteri ile * değiştiriyoruz. */ *str = '\0'; /* * "str" değişkeni artık '\0' karakterini, "str + 1" ise * '\0' karakterinin bir yanındaki karakteri gösteriyor. * Dolayısıyla o nokta, başlangıç noktası olarak ele alındı. */ if(setenv(argv[i], str + 1, 1) == -1) { perror("setenv"); } } } void get_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { if((str = getenv(argv[i])) == NULL) { fprintf(stderr, "environment variable not found: %s\n", argv[i]); continue; } printf("%s ---> %s\n", argv[i], str); } } * Örnek 4, #include #include #include #include void set_env_var(int argc, char* argv[]); void get_env_var(int argc, char* argv[]); void exit_sys(const char* msg); int main(int argc, char* argv[]) { /* # Command Line Arguments: # ali=100 ali=200 */ /* # OUTPUT # environment variable not found: ali=100 environment variable not found: ali=200 ali ---> 100 ali ---> 100 */ if(argc == 1) { fprintf(stderr, "too few arguments!...\n"); exit(EXIT_FAILURE); } get_env_var(argc, argv); set_env_var(argc, argv); get_env_var(argc, argv); return 0; } void set_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { /* * "argv[i]" yazısı içerisinde '=' karakteri arıyoruz. */ if((str = strchr(argv[i], '=')) == NULL) { fprintf(stderr, "invalid argument: %s\n", argv[i]); continue; } /* * Eğer bu karakter bulunmuşsa, programın akışı buraya * gelecektir. '=' karakterini '\0' karakteri ile * değiştiriyoruz. */ *str = '\0'; /* * "str" değişkeni artık '\0' karakterini, "str + 1" ise * '\0' karakterinin bir yanındaki karakteri gösteriyor. * Dolayısıyla o nokta, başlangıç noktası olarak ele alındı. */ if(setenv(argv[i], str + 1, 0) == -1) { perror("setenv"); } } } void get_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { if((str = getenv(argv[i])) == NULL) { fprintf(stderr, "environment variable not found: %s\n", argv[i]); continue; } printf("%s ---> %s\n", argv[i], str); } } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } >>> "putenv" fonksiyonu, yine "setenv" fonksiyonu gibi çevre değişkenlerine ekleme yapmaktadır. Fonksiyonun prototipi aşağıdaki gibidir; #include int putenv(char *string); Fonksiyonumuz görüldüğü üzere tek bir parametre almaktadır. Eşleştirme kısmını kendi bünyesinde halletmektedir. Aslında "bizim yukarıda elle yaptığımız şeyi yapmaktadır", diyebiliriz. Argüman olarak aldığı yazının içerisinde "=" olmalı ve "anahtar" var ise mutlaka buna karşılık gelen "değer" değiştirilmektedir. İş bu fonksiyona geçilen adres bilgisi, doğrudan prosesin çevre bilgisi olarak kullanılmakta. Dolayısıyla bu adres bilgisi programcı tarafından değiştirilmemesi gerekmektedir. Yani bu fonksiyona geçilen yazı direkt olarak çevre değişkeni olarak atanmaktadır. Programın ilerleyen safhalarında bu yazının bir biçimde DEĞİŞTİRİLMEMESİ GEREKİYOR. * Örnek 1, #include #include #include #include void set_env_var(int argc, char* argv[]); void get_env_var(int argc, char* argv[]); void exit_sys(const char* msg); int main(int argc, char* argv[]) { /* # Command Line Arguments: # ali=100 ali=200 */ /* # OUTPUT # environment variable not found: ali=100 environment variable not found: ali=200 environment variable not found: ali=100 environment variable not found: ali=200 */ if(argc == 1) { fprintf(stderr, "too few arguments!...\n"); exit(EXIT_FAILURE); } get_env_var(argc, argv); set_env_var(argc, argv); get_env_var(argc, argv); return 0; } void set_env_var(int argc, char* argv[]) { for(int i = 1; i < argc; ++i) if(putenv(argv[i]) == -1) perror("putenv"); /* * "putenv" fonksiyonunun ve çevre değişkenleri hakkında * detaylı bilgi ilerleyen konularda işleneceği için * şu an için bu kısım boş bırakılmıştır. */ } void get_env_var(int argc, char* argv[]) { char* str; for(int i = 1; i < argc; ++i) { if((str = getenv(argv[i])) == NULL) { fprintf(stderr, "environment variable not found: %s\n", argv[i]); continue; } printf("%s ---> %s\n", argv[i], str); } } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } UNIX/Linux sistemlerinde prosesin çevre değişkenleri, prosesin bellekteki adres alanı içerisindeki bir dizide tutulmaktadır. Bu dizi öyle bir dizi ki her bir indisindeki gösterici "anahtar=değer\0" biçimindeki yazıları göstermektedir. Bu dizinin en sonki elemanı da NULL değere sahip bir göstericidir. İş bu dizi ise "environ" isimli göstericiyi gösteren gösterici şeklinde olup, türü "char**" şeklindedir. Fakat bu değişkenin "extern" bildirimi yapılmadığı için, kullanmadan önce, bizler bu bildirimi yapmalıyız. Aşağıda temsili bir şeması çizilmiştir; environ(char**) ---> _pointer1_(char*) ---> ali=100\0 _pointer2_(char*) ---> vali=200\0 ... ---> ... NULL Aslında "putenv" fonksiyonu, argüman olarak aldığı yazıyı direkt olarak bu diziye enjekte etmektedir. Windows sistemlerindeki organizasyon tam olarak böyle değildir. * Örnek 1, "putenv" fonksiyonunun tipik kullanımı: #include #include #include #include void release_memory(char** env_vars, int argc, char* argv[]); char** create_memory(int argc, char* argv[]); void set_env_var(int argc, char* argv[]); void get_env_var(int argc, char* argv[]); int main(int argc, char* argv[]) { /* # Command Line Arguments: # ali=100 vali=200 */ /* # OUTPUT # environment variable not found: ali environment variable not found: vali Success! Success! ali ---> 100 vali ---> 200 */ if(argc == 1) { fprintf(stderr, "too few arguments!...\n"); exit(EXIT_FAILURE); } /* * Girilen komut satırları başka bir diziye kopyalandı. */ char** env_vars = create_memory(argc, argv); /* * Komut satırı argümanlarıyla ilk sorgulama yapıldı. */ get_env_var(argc, argv); /* * Daha sonra prosesin çevre değişkenleri listesine yeni * ekleme yapıldı. */ set_env_var(argc, env_vars); /* * Komut satırı argümanlarıyla son sorgulama yapıldı. */ get_env_var(argc, argv); /* * Kopyalanan dizi tekrar geri verildi. */ release_memory(env_vars, argc, argv); return 0; } void release_memory(char** env_vars, int argc, char* argv[]) { for(int i = 0; i < argc; ++i) { free(env_vars[i]); } free(env_vars); } char** create_memory(int argc, char* argv[]) { char** env_vars; env_vars = (char**)malloc(argc * sizeof(char*)); if(env_vars) { for(int i = 0; i < argc; ++i) { env_vars[i] = (char*)malloc(32 * sizeof(char)); if(env_vars[i]) { strcpy(env_vars[i], argv[i]); } } } return env_vars; } void set_env_var(int argc, char* argv[]) { /* * "argv" dizisinin her bir elemanı direkt olarak yeni * bir çevre değişkeni olarak eklenmektedir. */ for(int i = 1; i < argc; ++i) if(putenv(argv[i]) == 0) printf("Success!\n"); } void get_env_var(int argc, char* argv[]) { char* str; /* * Fakat sorgulama yapmak için dizi içerisindeki '=' * karakteri, '\0' karakteriyle değiştirilmelidir. */ for(int i = 1; i < argc; ++i) { if((str = strchr(argv[i], '=')) == NULL) { continue; } *str = '\0'; } /* * Artık dizinin ilk harfi ile birinci '\0' arasındakiler "anahtar" * olarak aranılacaktır. */ for(int i = 1; i < argc; ++i) { if((str = getenv(argv[i])) == NULL) { fprintf(stderr, "environment variable not found: %s\n", argv[i]); continue; } printf("%s ---> %s\n", argv[i], str); } } * Örnek 2, "environ" isimli değişkenin kullanımı: #include #include /* Aşağıdaki bildirimi bizlerin yapması zorunludur. */ extern char** environ; int main() { /* # OUTPUT # HOSTNAME=Check LANGUAGE=en_US:en PWD=/home HOME=/home/runner5 LANG=en_US.UTF-8 GOROOT=/usr/local/go TERM=xterm DISPLAY=:1 SHLVL=1 PS1=#ogdb"shell"# LC_ALL=en_US.UTF-8 PATH=/opt/swift/swift-5.0-RELEASE-ubuntu14.04/usr/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin DEBIAN_FRONTEND=noninteractive _=/script/tinit */ for(int i = 0; environ[i] != NULL; ++i) puts(environ[i]); return 0; } * Örnek 3, Temsili bir "getenv" implementasyonu: #include #include #include #include extern char** environ; void exit_sys(const char* msg); char* my_getenv(const char* name); int main() { /* # OUTPUT # /opt/swift/swift-5.0-RELEASE-ubuntu14.04/usr/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin */ char* val; if((val = my_getenv("PATH")) == NULL) { fprintf(stderr, "environment variable not found!...\n"); exit(EXIT_FAILURE); } puts(val); return 0; } char* my_getenv(const char* name) { char* str; /* * İlgili dizinin her bir indisindeki yazı incelenir: */ for(int i = 0; environ[i] != NULL; ++i) { /* * İlgili yazının içerisinde '=' karakteri mevcutsa, * "str" değişkeni bu karakteri gösteriyordur. */ if((str = strchr(environ[i], '=')) != NULL) /* * "environ[i]" adresi dizinin başlangıcı, "str" * ise '=' karakterini gösteriyorsa, bu iki göstericinin * farkı bize "anahtar" ın eleman sayısını verecektir. * "name" değişkeni ile dizinin başından itibaren * bu eleman sayısı kadarki kısmı karşılaştırırsak ve * eşit ise ilgili "anahtar" ı bulmuş olacağız. "str" den * bir sonraki eleman ise bize "değer" bilgisini verecektir. */ if(!strncmp(name, environ[i], str - environ[i])) return str + 1; } return NULL; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } Proseslere ilişkin çevre değişkenleri, prosese özgü bellek alanı içerisinde tahsis edilmiştir. Bir "fork" işlemi sırasında üst prosesin bellek alanı da kopyalandığı için, alt proses ile üst proses aynı çevre değişkenlerine sahip olacaklardır. Fakat "fork" işleminden sonra her iki prosesin çevre değişkenleri birbirinden bağımsız hareket edeceklerdir. Birisinde yapılan değişiklik diğer prosesinkini etkilemeyecektir. Pekiyi bizler belli bir çevre değişken ya da değişkenlerinin bizim programımıza geçmesini istiyorsak ne yapmalıyız? Bizim programımızı çalıştıran program her neyse, onun çevre değişkenlerini değiştirerek. Örneğin, "shell" programı üzerinden "export FRUIT=Banana" komutunu çalıştırdığımız zaman, "FRUIT=Banana" ikilisi artık "shell" programının bir çevre değişkeni olacaktır. Bunun bir diğer alternatifi de "FRUIT=Banana" ve "export FRUIT" komutlarını sırasıyla çağırmak olacaktır. Fakat unutmamalıyız ki bu durum geçici sürelidir. Başka bir "shell" programı çalıştırdığımız vakit, iş bu çevre değişkeni orada GÖZÜKMEYECEKTİR. Pekiyi bizler kalıcı bir şekilde bu soruna nasıl çözüm bulabiliriz? "shell" programı çalışmaya başlamadan evvel bir takım dosyalardan bilgi okuması yapmaktadır. Bu dosyalara genel hatlarıyla "shell" programının "start-up" dosyaları denmektedir. Diğer taraftan kabuk programının çevre değişkenlerini hiç değiştirmeden, doğrudan çalıştırılan programınkilerini değiştirebiliriz de. Bunun için "FRUIT=Banana ./our_program" şeklinde programımızı çalıştırmalıyız. Diğer taraftan bir "shell" programı üç farklı biçimde çalıştırılabilir, dolayısıyla iş bu "start-up" dosyaları da birbirinden farklıdır. Bir "shell" programı aşağıdaki biçimlerde çalıştırılabilir: >>> "Interactive Login 'shell'" biçiminde. Buradaki interaktiflik, komut satırına düşmeyi kastetmektedir. Yani bir "shell" programı üzerinden bir program çalıştırdıktan sonra, programın bitmesiyle beraber, imlecin tekrardan "shell" programında belirmesidir. Linux işletim sisteminde "CTRL + ALT F1" tuş kombinasyonlarına bastığımız vakit karşımıza çıkan siyah ekran, iş bu "shell" programıdır. Bu tür çalıştırmada dosya okuma sırası aşağıdaki gibidir; -> İlk olarak "/etc/profile" dosyası okunacaktır, eğer varsa. Olmaması durumunda bir sorun oluşmayacaktır. -> Daha sonra sırasıyla "~/.bash_profile", "~/.bash_login" ve "~/.profile" dosyaları ele alınır. Fakat sadece ilk bulduğu dosyadan okuma yapacaktır. Dolayısıyla bizler çevre değişkenlerimizi ilk olarak "~/.bash_profile" içerisine yazmalıyız. Buradaki "~" işareti "/home" dizinidir. Yine unutmamalıyız ki bu üç dosyaya sadece bu tip bir "shell" programı bakmaktadır. >>> "Interactive Non-Login "shell"" biçiminde. Buradaki "Login" durumu, "shell" programını çalıştırdığımız zaman bizden kullanıcı adı ve şifre sorması demektir. Bilgileri doğru girdikten sonra "shell" komutunu kullanmaya başlayabiliriz. Başlat çubuğuna "terminal" yazınca karşımıza gelen "shell" budur. Daha doğrusu en sık kullandığımız "shell" komutudur. Bu tür çalıştırmada dosya okuma sırası aşağıdaki gibidir; -> İlk olarak "~/.bashrc" dosyası okunacaktır, eğer varsa. >>> "Non-Interactive Login "shell"" biçiminde. "Non-Interactive" olması, tek bir komutu çalıştırıp çıkacak biçimde olmasıdır. Örneğin, "/bin/bash -c ls -l" komutunu normal bir "shell" programı üzerinden çalıştırığımız zaman, "/bin/bash" içerisindeki "shell" programı tek bir seferlik çalışacak, "ls -l" programını çalıştıracak ve daha sonra sonlanacaktır. Bu etkiyi oluşturan şey ise "-c" seçeneğinin geçilmesidir. Bu tür çalıştırmada dosya okuma sırası aşağıdaki gibidir; -> "BASH_ENV" isimli bir çevre değişkenini araştırılır ve buna karşılık gelen "script" dosyasını çalıştırır. Bütün bunlara ek olarak "Interactive Login "shell"" ile "Interactive Non-Login "shell"" tip "shell" programlarının aynı dosyaya bakmasını olanak vermek için "~/.bash_profile" dosyasına aşağıdaki komutu yazmalıyız. if [ -f ~/.bashrc ]; then . ~/.bashrc; fi Böylelikle "~/.bashrc" dosyasına yazacağımız komutlar ki "export FRUIT=Banana" bir örnek olarak gösterilebilir, her iki tip "shell" programı tarafından da kalıcı olarak bilinir hale gelecektir. Bir üç versiyon hakkındaki detaylara, aşağıdaki dökümandan ulaşabiliriz; https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Bash-Startup-Files > Çevre Değişkenlerinin Silinmesi: Bir çevre değişkeni "unsetenv" isimli POSIX fonksiyonu ile listeden silinebilmektedir. Fonksiyonun prototipi şöyledir; #include int unsetenv(const char *name); Parametre olarak çevre değişkeninin ismini almakta. Başarı durumunda "0" ile başarısızlık durumunda "-1" ile geri döner ve "errno" uygun şekilde değiştirilir. * Örnek 1, #include #include #include #include extern char** environ; void exit_sys(const char* msg); int main(int argc, char** argv) { /* # Command Line Arguments: # PWD */ /* # OUTPUT # /home environment variable not found: PWD */ if(argc != 2) { fprintf(stderr, "wrong number of arguments!...\n"); exit(EXIT_FAILURE); } char* value; if((value = getenv(argv[1])) == NULL) { fprintf(stderr, "environment variable not found: %s\n", argv[1]); exit(EXIT_FAILURE); } puts(value); if(unsetenv(argv[1]) == -1) exit_sys("unsetenv"); if((value = getenv(argv[1])) == NULL) { fprintf(stderr, "environment variable not found: %s\n", argv[1]); exit(EXIT_FAILURE); } puts(value); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } > Çevre Değişkenlerine Duyulan Gereksinim: Bir takım aşağı seviyeli işlemlerin parametrik hale getirilmesi için kullanılmaktadır. Yani bu aşağı seviyeli bazı işlemlerin basit biçimde dışarıdan değiştirilmesine olanak sağlamaktır. Bazı çevre değişkenleri, bazı POSIX fonksiyonları tarafından da kullanılmaktadır. Örneğin "exec" fonksiyonlarının "p" li biçimleri, iş bu çevre değişkenlerine başvurmaktadır. Ek olarak, dinamik bir kütüphane yüklerken "LD_LIBRARY_PATH" isimli çevre değişkenine de başvurulmaktadır. Zaman zaman da uygulama programcıları bu çevre değişkenlerini kullanmaktadır. Örneğin, "gcc" derleyicisi "<>" ile eklenen başlık dosyalarının isimlerini "C_INCLUDE_PATH" isimli çevre değişkeninde de aramaktadır. Aşağıda örnek bir komut verilmiştir; "export C_INCLUDE_PATH=/home/kaan:/home/kaan/Study" Artık "gcc" derleyicisi hem "/home/kaan" dizinine hem de "/home/kaan/Study" dizinine ilgili başlık dosyaları için bakacaktır. > Hatırlatıcı Notlar: >> "getenv" fonksiyonu ile çevre değişkenlerini DEĞİŞTİRMEYE ÇALIŞMAMALIYIZ. Çevre değişkenlerini değiştirmek için önce "getenv" ile bilgileri temin ediyoruz. Sonrasında bir tampon üzerinde bu değişkenleri değiştiriyoruz. Son olarak "setenv" fonksiyonu ile bu tampondakileri yeni çevre değişkenleri olarak değiştiriyoruz. Eğer parametre olarak geçilen yazının içerisinde "=" olmaması durumunda, "değer" bilgisi boş bir yazı olacaktır. Fonksiyon başarı durumunda "0" ile başarısızlık durumunda "-1" ile geri dönmektedir ve "errno" değiştirilmektedir. >> UNIX/Linux sistemlerinde çevre değişkenleri büyük harf/küçük harf duyarlılığına sahipken, Windows sistemlerinde böyle bir duyarlılık yoktur. >> "PATH" çevre değişkeninin elde edilmesi: * Örnek 1, #include #include #include #include void exit_sys(const char* msg); int main(int argc, char** argv) { /* # OUTPUT # /opt/swift/swift-5.7.3-RELEASE-ubuntu22.04/usr/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin */ char* path; path = getenv("PATH"); puts(path); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } >> "PATH" çevre değişkenini "shell" programı vasıtasıyla değiştirmek için şu işlemler uygulanmalıdır: -> Önce "echo $PATH" ile ilgili çevre değişkeni ekrana basılır. -> Daha sonra "PATH=$PATH:." çağrısı yapılmalıdır. Artık "PATH" değişeninin sonuna ":." karakterleri eklenmiştir. "shell" programını yeniden çalıştırana kadar veya yeni bir "shell" programı çalıştırana kadar "exec" fonksiyonlarının "p" li versiyonları, prosesin o anki çalışma dizinine de ilgili isim için bakacaklardır. Kalıcı bir değişiklik için "shell" programının "start-up" dosyalarında oynama yapmamız gerekmektedir. Fakat proseslerin çalışma dizinlerinin "PATH" çevre değişkenine eklenmesi, güvenlik zaafiyeti oluşturabileceği için, iyi bir teknik değildir.