Shell Script #4 – Bash’in Yapılandırma Dosyaları

Hayat kurtarmak için hazırda bekleyen shell’imiz, verdiğimiz komutları yorumluyor ve çalıştırıyor. Shell’imiz şu özelliklere sahip:

  • Bir program çalıştırabiliyor. Harici komutlar (external commands) bunun örneği: ls, cat, ps gibi.
  • Kendisi, önceden tanımlamış komutları çalıştırabiliyor. Dâhili komutlar (internal commands) bunun örneği: type, help, echo gibi.
  • Aynı zamanda kendisi de bir programlama ortamı. Shell script’leri yazabiliyor ve bu scriptler içinde döngüler (for, while gibi), karar yapıları (if-else gibi) bunun örneği.

Bu açıdan bakarsak shell’imiz, gerçek anlamda bizim çalışma ortamımız. Black is the new gold. Bu yazıda, “bash” programının davranışlarını etkileyen ayar dosyalarından bahsedeceğiz.

Başlamadan önce tekrar vurgulamakta fayda görüyorum. “bash” shell’i için konuşuyoruz. Farklı shell’ler kullanıyorsanız farklı dosyalarla haşır neşir olmanız kuvvetle muhtemel.

Cihazınızda oturum açtığınız anda, bash bazı script’leri çalıştırır ve çalışma ortamınızı hazırlar. Bu script’leri 2 ana kategoriye ayırabiliriz:

Sistem Geneli Yapılandırma DosyalarıKullanıcı Bazlı Yapılandırma Dosyaları
/etc/profile~/.bash_profile
/etc/bashrc~/.bashrc
~/.bash_login
~/.profile
GNU/Linux işletim sisteminde bash programının yapılandırma dosyaları

Tabii ki bu dosyaların tamamı sisteminizde bulunmak zorunda değil. Bazılarını, farklı amaçlar için kendiniz de oluşturabilirsiniz. Şimdi bu dosyaların içeriklerine göz atalım ve amaçlarını anlamaya çalışalım.

/etc/profile Dosyası Nedir?

Bu dosya, yalnızda “root” kullanıcısının kontrolündedir. Sisteme login olan tüm kullanıcılar için çalıştırılır. “Sadece login shell’lerinde” çalıştırılır. Ne demek istediğimi örnekleyeceğim. Önce dosya içeriğini inceleyelim:

[root@localhost ~]# cat /etc/profile
# /etc/profile

# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

pathmunge () {
    case ":${PATH}:" in
        *:"$1":*)
            ;;
        *)
            if [ "$2" = "after" ] ; then
                PATH=$PATH:$1
            else
                PATH=$1:$PATH
            fi
    esac
}


if [ -x /usr/bin/id ]; then
    if [ -z "$EUID" ]; then
        # ksh workaround
        EUID=`/usr/bin/id -u`
        UID=`/usr/bin/id -ru`
    fi
    USER="`/usr/bin/id -un`"
    LOGNAME=$USER
    MAIL="/var/spool/mail/$USER"
fi

# Path manipulation
if [ "$EUID" = "0" ]; then
    pathmunge /usr/sbin
    pathmunge /usr/local/sbin
else
    pathmunge /usr/local/sbin after
    pathmunge /usr/sbin after
fi

HOSTNAME=`/usr/bin/hostname 2>/dev/null`
HISTSIZE=1000
if [ "$HISTCONTROL" = "ignorespace" ] ; then
    export HISTCONTROL=ignoreboth
else
    export HISTCONTROL=ignoredups
fi

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL

# By default, we want umask to get set. This sets it for login shell
# Current threshold for system reserved uid/gids is 200
# You could check uidgid reservation validity in
# /usr/share/doc/setup-*/uidgid file
if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
    umask 002
else
    umask 022
fi

for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then
            . "$i"
        else
            . "$i" >/dev/null
        fi
    fi
done

unset i
unset -f pathmunge

Kabaca göz attığımızda şunları görüyoruz:

  • $PATH değişkeni tanımlanıyor.
  • $USER, $LOGNAME, $MAIL değişkenleri tanımlanıyor.
  • $HOSTNAME değişkeni tanımlanıyor.
  • $HISTSIZE ile bash’in geçmişi ne kadar tutacağı tanımlanıyor.
  • Varsayılan umask değeri ayarlanıyor.
  • /etc/profile.d/ dizinindeki script’ler çalıştırılıyor.

Bu dosyayla ilgili dikkat etmemiz gereken bazı noktalar var:

  • “Ne yaptığınızı bilmiyorsanız müdahale etmeyin.” uyarısını görüyoruz.
  • Yeni bir ayar istiyorsak, bunu doğrudan bu dosya içine değil, /etc/profile.d/ dizini altında yeni bir dosyaya yazmalıyız.
  • Fonksiyon ve alias‘ları buraya değil, /etc/bashrc altına tanımlamalıyız.

Gelelim login shell ile ne kast ettiğime. Bu script’in en altına bir komut ekleyeceğim: “echo ‘Merhaba. Beni buraya /etc/profile yazdı'”

Böylece bash her çalıştığında bu çıktıyı görmek isteriz değil mi?

Login shell'de /etc/profile davranışı
Login shell’de /etc/profile davranışı

Çok güzel. Gayet tatlı çalıştı. Çünkü bu bir “login shell”di. Oturum açabileceğimiz, bize kullanıcı adı – parola kombinasyonu soran bir shell. Benzer şekilde SSH yaptığımızda da bu mesajı göreceğiz.

Peki yeni bash’ler başlattığımızda ne olacak? Başlatacağımız yeni bash’ler, birer login shell olmayacağı için bu dosyayı çalıştırmayacak ve sonuç aşağıdaki gibi olacak:

/etc/profile yalnızda login shell'lerde çalışır.
/etc/profile yalnızda login shell’lerde çalışır.

Gördüğünüz gibi 1451 PID’li login shell’imiz altında 3 tane daha bash başlattık. Fakat bu başlangıçların hiçbirinde mesajımızı göremedik.

Login shell ve non-login shell arasındaki farkı daha iyi, daha detaylı anlamak isterseniz burası iyi gelebilir.

/etc/bashrc Dosyası Nedir?

profile dosyası, fonksiyon ve alias’lar için bizi bu dosyaya yönlendirmişti hatırlarsanız. İçeriğine bir göz atalım:

[root@localhost ~]# cat /etc/bashrc
# /etc/bashrc

# System wide functions and aliases
# Environment stuff goes in /etc/profile

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

# are we an interactive shell?
if [ "$PS1" ]; then
  if [ -z "$PROMPT_COMMAND" ]; then
    case $TERM in
    xterm*|vte*)
      if [ -e /etc/sysconfig/bash-prompt-xterm ]; then
          PROMPT_COMMAND=/etc/sysconfig/bash-prompt-xterm
      elif [ "${VTE_VERSION:-0}" -ge 3405 ]; then
          PROMPT_COMMAND="__vte_prompt_command"
      else
          PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
      fi
      ;;
    screen*)
      if [ -e /etc/sysconfig/bash-prompt-screen ]; then
          PROMPT_COMMAND=/etc/sysconfig/bash-prompt-screen
      else
          PROMPT_COMMAND='printf "\033k%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
      fi
      ;;
    *)
      [ -e /etc/sysconfig/bash-prompt-default ] && PROMPT_COMMAND=/etc/sysconfig/bash-prompt-default
      ;;
    esac
  fi
  # Turn on parallel history
  shopt -s histappend
  history -a
  # Turn on checkwinsize
  shopt -s checkwinsize
  [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ "
  # You might want to have e.g. tty in prompt (e.g. more virtual machines)
  # and console windows
  # If you want to do so, just add e.g.
  # if [ "$PS1" ]; then
  #   PS1="[\u@\h:\l \W]\\$ "
  # fi
  # to your custom modification shell script in /etc/profile.d/ directory
fi

if ! shopt -q login_shell ; then # We're not a login shell
    # Need to redefine pathmunge, it get's undefined at the end of /etc/profile
    pathmunge () {
        case ":${PATH}:" in
            *:"$1":*)
                ;;
            *)
                if [ "$2" = "after" ] ; then
                    PATH=$PATH:$1
                else
                    PATH=$1:$PATH
                fi
        esac
    }

    # By default, we want umask to get set. This sets it for non-login shell.
    # Current threshold for system reserved uid/gids is 200
    # You could check uidgid reservation validity in
    # /usr/share/doc/setup-*/uidgid file
    if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
       umask 002
    else
       umask 022
    fi

    SHELL=/bin/bash
    # Only display echos from profile.d scripts if we are no login shell
    # and interactive - otherwise just process them to set envvars
    for i in /etc/profile.d/*.sh; do
        if [ -r "$i" ]; then
            if [ "$PS1" ]; then
                . "$i"
            else
                . "$i" >/dev/null
            fi
        fi
    done

    unset i
    unset -f pathmunge
fi
# vim:ts=4:sw=4

alias pro='. /root/proxy.sh'

Karşımıza şunlar geliyor:

  • Sistem genelinde kullanılan fonksiyon ve alias’lar burada yer alıyor.
  • Çevre değişkenleri (environment variables) ile ilgili işlerin /etc/profile’a gitmesi gerektiği belirtilmiş.
  • Ve yine; mümkünse elinizi sürmeyin ve ayarlarınızı, /etc/profile.d/ altına oluşturacağınız yeni script’lerde yapın uyarısı verilmiş.
  • Shell prompt’umuzla ilgili bazı ayarlar yapılmış.
  • PATH ve umask için yine bir değer ataması var. Çünkü /etc/profile ile login shell’lerinde bu ayarları yapmıştık. Non-login shell’de ise burada yazanlar geçerli olacak.
  • En altta da, proxy değiştirmek için kullandığım script’im için bir alias tanımlamışım. Böylece her “pro” yazdığımda, bash gidip benim hazırladığım proxy script’ini çalıştıracak.

.bash_profile ve .bashrc Dosyaları Nedir?

Kullanıcı oturum açtığında, bu dosyalardan biri çalıştırılarak kullanıcı ortamını hazırlar.

.bash_profile” dosyası login shell’lerde çalışırken, “.bashrc” dosyası non-login shell’lerde çalışır. Bu iki dosya da, kullanıcının ev dizininde bulunur.

.bash_profile Dosyasının İçeriği

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin

export PATH

export GPG_TTY=$(tty)

Alias ve fonksiyonların login olduğumuz anda gelebilmesi için “.bashrc” dosyasının varlığı kontrol edilmiş. Sonrasında bu script, bu shell’de geçerli olacak şekilde çalıştırılmış. Baştaki noktanın olayı bu.

Her kullanıcının ev dizininin farklı olması beklendiği için, burada PATH değişkenine kullanıcının ev dizini altında bulunması muhtemel “bin” dizini de eklenmiş. Bu ayarı “/etc/profile” altında yapsaydık, tüm kullanıcılar path’inde aynı ev dizinini görürdü.

Son olarak GitHub’da verified commit yapabilmek için “GPG_TTY” değişkenini ayarlamışım.

.bashrc Dosyasının İçeriği

# .bashrc

# User specific aliases and functions

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'


# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

Tanımlanmış 3 tane alias görüyoruz. rm, cp ve mv programlarının tamamı; varsayılan olarak “-i” parametresiyle başlatılmış. Mevzular böyle işte arkadaşlar. “Abi RHEL ve CentOS’ta silmeden önce silinsin mi diye soruyor. Diğer dağıtımlar sormuyor. Dağıtım farkı işte.” demeden önce, açıp varsayılan olarak tanımlanmış alias’lara bir bakmakta fayda var. Bu dağıtım, bu dosya içinde bu alias’ları tanımlamış. Yoksa rm hâlâ aynı rm. Değişen bir şey yok.

Bu dosyanın da “/etc/bashrc” dosyasını çağırdığını görüyoruz. Bu bağlantılar çok önemli. Globaldeki her şeyi alıyoruz. Üzerine şahsi değişikliklerimizi yapıyoruz. Eğer globaldeki ayarları almazsak shell’imiz sağlıklı çalışmayacaktır.

.bash_login Dosyası Nedir?

Login olduğumuzda profile dosyaları çalıştırıyordu. “/etc/profile” sistem geneli için, “~/.bash_profile” ise kullanıcıya özel. “.bash_login” dosyası ise, “.bash_profile” dosyasının bulunmadığı zamanlarda çalıştırılan bir script. Kullanıcı login olduğunda yapılacak tanımlamalar ve çalıştırılacak programları içerir. Muhtemelen sisteminizde böyle bir dosya yoktur. Bunun yerine “.bash_profile” oluşturulmuştur.

.profile Dosyası Nedir?

.bash_profile” ve “.bash_login” dosyalarının bulunmadığı zamanlarda çalıştırılır. Amacı aynıdır. Muhtemelen bu dosya da sisteminizde bulunmuyordur.

Shell’imizi Biraz Kişiselleştirelim

Oturum açtığımızda sistemimizin bizi karşılamasını istiyoruz. Bu durum, yalnızca bizim kullanıcımız için geçerli olsun.

Böyle bir durumda “/etc” altındaki dosyalardan uzak duracağız. Çünkü o dosyalardaki ayarlar sistem geneline uygulanıyor. Bu karşılama durumu, login olduğumuzda gerçekleşsin istiyoruz. İlgileneceğimiz dosya “~/.bash_profile” olacak:

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin

export PATH

export GPG_TTY=$(tty)

#Karşılama kısmımız burada başlıyor:

echo Hoş geldin $USER, $HOSTNAME cihazına bağlandın.
echo Bugün: $(date)
echo "Kullandığın DNS'ler:"
cat /etc/resolv.conf | grep nameserver | cut -d " " -f 2

“Karşılama kısmımız burada başlıyor:” satırının altından itibaren kodlarımı yazdım ve kaydettim. Bir sonraki login’de durum şuna dönüşüyor:

.bash_profile değişikliğinden sonra login ekranı.
.bash_profile değişikliğinden sonra login ekranı.

Kodun açıklamalı hâlini GitHub’daki repo’da bulabilirsiniz (4-bash_profile_ornegi.sh).

Dikkat ettiyseniz, yeni açılan shell’lerde karşılama mesajımızı göremiyoruz. Çünkü bu shell’ler bir login shell değil.

.bashrc Altına Alias Tanımlama

Bir de alias tanımlayalım ve kullanmayı deneyelim. Çalışmayacak. Baştan söyleyeyim.

Alias’larımızı “.bashrc” altına tanımlıyorduk:

# .bashrc

# User specific aliases and functions

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

#Buraya yeni bir alias tanımlıyorum:
alias ls='ls -alhi'

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

“Buraya yeni bir alias tanımlıyorum:” satırının altında, ls için bir alias tanımlamışım.

Şimdi aşağıdaki çıktıyı ve ekran görüntüsünü inceleyelim:

[root@localhost ~]# type ls
ls `ls --color=auto' için takma addır
[root@localhost ~]# ls
bashscript  degiskenler.sh  projeler  proxy.sh
[root@localhost ~]# source .bashrc
[root@localhost ~]# type ls
ls `ls -alhi' için takma addır
[root@localhost ~]# ls
toplam 72K
33582145 dr-xr-x---.  7 root root 4,0K Oca 31 19:14 .
      64 dr-xr-xr-x. 17 root root  224 Oca 26 14:59 ..
33582149 -rw-------.  1 root root  20K Oca 31 19:15 .bash_history
33582196 -rw-r--r--.  1 root root   17 Ara 16 21:08 .bash_logout
34705792 -rw-r--r--.  1 root root  200 Oca 31 19:13 .bash_profile
33582201 -rw-r--r--.  1 root root  236 Oca 31 19:14 .bashrc
  775334 drwxr-xr-x.  2 root root   88 Oca 22 22:06 bashscript
34022782 -rw-r--r--.  1 root root  100 Ara 29  2013 .cshrc
33582200 -rwxr--r--.  1 root root   70 Ara 16 21:41 degiskenler.sh
33582202 -rw-r--r--.  1 root root  154 Oca 30 17:43 .gitconfig
33865371 -rw-------.  1 root root   71 Oca 30 18:16 .git-credentials
50671788 drwx------.  3 root root  257 Oca 30 18:16 .gnupg
33582199 -rw-------.  1 root root   86 Oca 29 14:17 .lesshst
33987585 -rw-------.  1 root root  476 Ara  5 16:27 .mysql_history
51139467 drwxr-----.  3 root root   19 Ara  5 16:05 .pki
33582188 drwxr-xr-x.  5 root root   82 Oca 30 17:46 projeler
33627844 -rwxr--r--.  1 root root  509 Oca 29 13:20 proxy.sh
34705992 -rw-------.  1 root root   44 Oca 29 16:48 .python_history
33582197 drwx------.  2 root root   57 Oca 26 03:14 .ssh
34022783 -rw-r--r--.  1 root root  129 Ara 29  2013 .tcshrc
ls için alias değişikliği
ls için alias değişikliği

Satır satır inceleyelim:

  1. “ls” nedir diye sorduk. “ls –color=auto” için takma addır (alias) yanıtını aldık. Sistem üzerinde ls programının renkli çalışmasını sağlayan alias budur. Yoksa dizinler mavi olacakmış falan, böyle bir şey yok. Her “ls” yazdığımızda – tıpkı rm -i örneğinde olduğu gibi – böyle davranması için hazırlanmış.
  2. ls çalıştırıyorum. Bildiğimiz ls çıktısı veriyor. Fakat ben “.bashrc” altında alias tanımladım. Neden benim tanımladığım alias yok? Neden benim belirttiğim parametrelerle çalışmadı? Çünkü bu yapılandırma dosyaları, bash ilk çalıştırıldığında çalışır. Hâli hazırda açık olan bir programın yapılandırmasını değiştirmek anlamsızdır. Çünkü bash, bu dosyaları çoktan okudu ve bitirdi. Peki değişiklik yaptığımızda ne olacak?
  3. “Kapatıp aç”, “Oturumu kapat tekrar gir” gibi şeyler duyabilirsiniz. Lazım olduğu yerler belki vardır fakat burası o anlardan biri değil. Burada yardımımıza “source” koşuyor. “source” komutunun amacı, bir dosyadaki ayarları “şu an bulunduğunuz shell’e” okumaktır. Verdiğiniz ayar script’ini kullanarak shell’inizi tekrar günceller. Bunun yerine “. .bashrc” de kullanabilirdik.
  4. type, bizim alias’ımızı gösteriyor.
  5. ls, alias’ta belirttiğimiz gibi “ls -alhi” çalıştırıyor.