Login skripte — šta se izvršava pri prijavi na sistem

Alias radi u terminalu, ali ne radi u skripti. Komanda se izvršava kad je kucaš rukom, a u cronu javlja da ne postoji. Podesio si promenljivu, a preko SSH-a je nema.

Sve tri situacije imaju isti uzrok: bash ne čita uvek iste fajlove. Koje će pročitati, zavisi od toga kako je pokrenut. Ova stranica objašnjava taj redosled — a kad ga jednom razumeš, sve tri zagonetke nestaju.

Dve podele koje sve određuju

Bash se pokreće u jednoj od četiri kombinacije, po dva pitanja:

Da li je login shell? Jeste kad se prijaviš na sistem — preko SSH-a, na konzoli, ili sa su -. Nije kad otvoriš novi prozor terminala u već pokrenutoj grafičkoj sesiji.

Da li je interaktivan? Jeste kad ti daje prompt i čeka da kucaš. Nije kad izvršava skriptu.

Proveri gde si trenutno:

$ shopt -q login_shell && echo "login" || echo "nije login"
nije login

$ echo $-
himBHs

Slovo i u drugom ispisu znači interaktivan. Ako ga nema, nije.

Redosled izvršavanja

Login shell (SSH, konzola, su -)

1. /etc/profile
2. /etc/profile.d/*.sh          (poziva ih /etc/profile)
3. prvi koji postoji od:
      ~/.bash_profile
      ~/.bash_login
      ~/.profile

Treća stavka je najvažnija zamka: čita se samo prvi pronađeni. Ako imaš i .bash_profile i .profile, drugi se nikada ne izvršava. Ljudi izgube sate podešavajući fajl koji sistem uopšte ne gleda.

Interaktivan, ali ne login (novi prozor terminala)

1. /etc/bash.bashrc          (na Debian i Ubuntu sistemima)
2. ~/.bashrc

Obrati pažnju: /etc/profile se ovde ne čita. Zato promenljiva podešena samo tamo ne postoji u novom prozoru terminala.

Neinteraktivan (skripta, cron)

Ništa — osim ako je postavljena promenljiva BASH_ENV

Ovo je odgovor na pitanje zašto cron posao ne vidi tvoja podešavanja. Skripta pokrenuta iz crona ne čita ni .bashrc ni .bash_profile.

Odjava

~/.bash_logout               (samo pri izlasku iz login shell-a)

Zašto skoro svaki .bash_profile ima ovaj blok

if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

Bez njega bi SSH sesija čitala .bash_profile, ali ne i .bashrc — pa aliasi ne bi radili. Sa njim se čitaju oba, u svakoj situaciji.

Otud i praktična podela koje se vredi držati:

~/.bash_profile Stvari koje se rade jednom po prijavi: promenljive okruženja, PATH, pokretanje agenta za ključeve.
~/.bashrc Stvari koje trebaju svakom shell-u: aliasi, funkcije, izgled prompta, podešavanja istorije.

Razlog za podelu: promenljive se nasleđuju u potprocesima, pa ih nema smisla postavljati iznova. Aliasi se ne nasleđuju, pa moraju svaki put.

Sistemski fajlovi

/etc/profile.d/ — pravo mesto za izmene

$ ls /etc/profile.d/
bash_completion.sh  locale.sh  vte.sh  Z97-byobu.sh

Kad podešavaš nešto za sve korisnike, ne diraj /etc/profile — nadogradnja sistema ga može prepisati. Umesto toga dodaj sopstveni fajl:

$ sudo tee /etc/profile.d/firma.sh > /dev/null << 'EOF'
export JAVA_HOME=/opt/java
export PATH="$PATH:/opt/firma/bin"
EOF

Ostaje netaknut pri nadogradnjama, a lako se ukloni. Nastavak .sh je obavezan — fajlovi bez njega se ignorišu.

/etc/environment — nije skripta

$ cat /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
LANG=sr_RS.UTF-8

Ovaj fajl čita PAM, ne bash, i to pre svega ostalog. Zbog toga u njemu:

  • nema reči export
  • nema proširivanja promenljivih — PATH="$PATH:/opt/bin" ovde ne radi, upisaće se doslovno
  • nema uslova, petlji ni bilo kakve logike

Prednost je što važi i za grafičke aplikacije i za servise, ne samo za shell.

/etc/skel — šabloni za nove korisnike

$ ls -a /etc/skel
.  ..  .bash_logout  .bashrc  .profile

Sadržaj se kopira u kućni direktorijum svakog novog naloga. Ako želiš da svi budući korisnici dobiju određeni alias, dodaj ga ovde. Detaljnije na stranici /etc/skel, a podešavanja pri kreiranju naloga na stranici /etc/login.defs.

Kako otkriti šta se zapravo izvršava

Ubaci privremeni ispis u svaki fajl:

$ echo 'echo "--- procitan .bash_profile"' >> ~/.bash_profile
$ echo 'echo "--- procitan .bashrc"' >> ~/.bashrc
$ echo 'echo "--- procitan .profile"' >> ~/.profile

Zatim isprobaj različite situacije:

$ bash -l -c exit
--- procitan .bash_profile
--- procitan .bashrc

$ bash -i -c exit
--- procitan .bashrc

$ bash -c exit
$

Poslednji red je prazan — neinteraktivna skripta ne čita ništa. Kad završiš, obriši te redove.

Isto proverava i preko SSH-a:

$ ssh server                      # login, interaktivan
$ ssh server 'echo test'          # ni login ni interaktivan

Druga komanda ne čita nijedan od tvojih fajlova. Zato skripte koje se pokreću ovako moraju koristiti apsolutne putanje — što je i tema lekcije Apsolutne i relativne putanje.

Tri greške koje se skupo plaćaju

1. Interaktivna komanda u .bashrc

Ovo izgleda bezopasno:

# ~/.bashrc
echo "Dobrodosao, $USER!"
fortune

A onda:

$ scp fajl.txt server:/tmp/
protocol error: unexpected <newline>

scp, sftp i rsync očekuju čist protokol na tom kanalu. Svaki tvoj ispis im pokvari razgovor. Isto važi i za git push preko SSH-a.

Rešenje je provera interaktivnosti na vrhu .bashrc:

case $- in
    *i*) ;;
      *) return;;
esac

Ako shell nije interaktivan, fajl se prekida odmah. Većina distribucija ovaj blok već ima na vrhu .bashrc — nemoj ga uklanjati, i sve svoje dodaj ispod njega.

2. exit u login skripti

# ~/.bash_profile
if [ -z "$DISPLAY" ]; then
    exit
fi

Ovo te zaključava sa sistema. Prijaviš se, skripta se izvrši, sesija se odmah zatvori — i to preko SSH-a, na konzoli, svuda.

Zato pri svakoj izmeni ovih fajlova drži otvorenu drugu sesiju. Postojeća sesija nastavlja da radi; kroz nju popravljaš grešku. Ako druge sesije nema, spas je prijava sa drugim shell-om:

$ ssh -t server /bin/sh

3. Greška u sintaksi

$ ssh server
bash: /home/alen/.bashrc: line 42: syntax error near unexpected token `fi'

Sesija radi, ali su podešavanja od tog reda naniže preskočena. Uvek proveri pre nego što se osloniš:

$ bash -n ~/.bashrc
$ echo $?
0

Opcija -n proverava sintaksu bez izvršavanja. Isti pristup kao nginx -t pre restarta servisa, o čemu piše na stranici Tekstualni editori.

Primena izmena bez odjave

$ source ~/.bashrc
$ . ~/.bashrc

Oba oblika rade isto — tačka je prenosiva na sve shell-ove, a source je čitljiviji.

Razlika koju treba razumeti:

$ ./skripta.sh        # potproces, izmene ostaju u njemu
$ source skripta.sh   # tekući shell, izmene ostaju kod tebe

Zato pokretanje ./podesi.sh ne menja tvoje okruženje, a source podesi.sh menja. Ovo je uzrok mnogih „skripta je radila, ali se ništa nije promenilo" situacija.

Posebni slučajevi

su sa crticom i bez nje

$ su marko          # zadržava tvoje okruženje, čita samo .bashrc
$ su - marko        # puna prijava, čita profile fajlove

Druga varijanta je gotovo uvek ono što želiš — inače korisnik dobija tuđi PATH i tuđe promenljive. Detaljnije na stranici su.

Isto važi i za sudo:

$ sudo komanda        # čisto okruženje, po pravilima u sudoers
$ sudo -i             # kao da si se root prijavio

Grafičko okruženje

Prijava kroz grafički menadžer ne pokreće bash kao login shell na isti način. Zato promenljiva podešena u .bash_profile često ne postoji u aplikacijama pokrenutim iz menija. Za takva podešavanja koristi /etc/environment ili ~/.config/environment.d/.

Nije bash

$ echo $SHELL
/usr/bin/zsh

Ako ti je podrazumevani shell zsh, ništa od gore navedenog ne važi — tamo su .zshrc i .zprofile. Koji ti je shell dodeljen, piše u /etc/passwd, u poslednjoj koloni.

tmux i screen

tmux podrazumevano pokreće login shell u svakom novom prozoru, pa se profile fajlovi čitaju iznova. To je i razlog zašto se PATH unutar tmux-a ponekad razlikuje — ako mu se nešto dodaje bez provere, uz svaki prozor se ponovi. Više o tmux-u na stranici screen i tmux.

Kratka referenca

Redosled

  • Login: /etc/profile/etc/profile.d/*.sh → prvi od ~/.bash_profile, ~/.bash_login, ~/.profile
  • Interaktivan, ne login: /etc/bash.bashrc~/.bashrc
  • Skripta: ništa (osim BASH_ENV)
  • Odjava: ~/.bash_logout

Šta gde ide

  • ~/.bash_profile — promenljive, PATH, jednokratne radnje
  • ~/.bashrc — aliasi, funkcije, prompt
  • /etc/profile.d/ime.sh — sistemski, otporno na nadogradnje
  • /etc/environment — bez export, bez logike, važi i van shell-a

Alati

  • shopt -q login_shell — da li je login shell
  • echo $- — slovo i znači interaktivan
  • bash -n fajl — provera sintakse
  • source ~/.bashrc — primeni bez odjave
  • bash -l -c exit, bash -i -c exit — test redosleda

Vežba

Prvo otvori drugu sesiju i ostavi je otvorenom.

  1. Ubaci prepoznatljiv ispis u sva tri korisnička fajla i utvrdi koji se čitaju pri SSH prijavi, a koji pri otvaranju novog prozora terminala.
  2. Napravi alias u ~/.bash_profile umesto u ~/.bashrc, pa objasni sebi zašto ne radi u novom prozoru.
  3. Postavi promenljivu u ~/.bashrc i proveri da li je vidi komanda pokrenuta sa ssh localhost 'echo $PROMENLJIVA'.
  4. Dodaj sistemsko podešavanje kroz /etc/profile.d/ i proveri da ga vidi i drugi korisnik.
  5. Napravi namernu grešku u sintaksi u ~/.bashrc, otvori novu sesiju da vidiš poruku, pa je popravi iz sesije koju si ostavio otvorenom.

Peti zadatak je vežba navike, ne komande.


Korisnički nalozi i njihova podrazumevana podešavanja su na stranicama /etc/passwd, /etc/skel i /etc/login.defs, a sopstvene prečice na stranici alias.

Cela lekcija je deo vodiča Linux osnove.

Comments

Popular posts from this blog

Početak u Linuxu — šta je i zašto se uči

Konverzija tipova podataka u Pythonu

groupadd