PyLadies Brno

Soubory, adresáře, a příkazová řádka do hloubky

Dnes si povíme, jak fungují systémy založené na UNIXu, jako Linux a macOS.
Windows fungují podobně, takže se tyhle znalosti budou hodit všem. Uvítám „překlad” tohoto textu, aby byl pochopitelnější pro uživatelky Windows – teď jsou instrukce pro Windows v takovýchto poznámkách.

Soubory a adresáře

UNIX je založený na souborech (angl. file) a procesech (angl. process). Zjednoušeně řečeno, soubor je něco z čeho se dají číst informace nebo je tam zapisovat; proces je program který právě běží – a může s informacemi manipulovat.
Soubory jsou uspořádané v adresářích neboli složkách (angl. directory, folder). Adresář je zvláštní typ souboru, který může obsahovat další soubory (a adresáře). Každý soubor a každý adresář tak někam „patří”.
Existuje takzvaný kořenový adresář (angl. root directory), označovaný lomítkem (/), který (nepřímo) obsahuje všechny soubory v systému. Máš-li unixový systém, můžeš se podívat, co obsahuje:
$ ls /
bin   data  etc   lib    lost+found  mnt  proc  run   srv  tmp  var
boot  dev   home  lib64  media       opt  root  sbin  sys  usr
Neboli: na příkazové řádce, za výzvu $, napiš příkaz ls (vypiš adresář), mezeru a / (jméno adresáře, který chceš vypsat), a stiskni Enter. Vypíše se seznam adresářů, které v / jsou. U tebe může seznam být trošku jiný.
Na Windows je situace jiná: neexistuje jeden adresář, ve kterém je všechno; místo toho má každý disk svůj kořenový adresář, jako C:, D:, E:, atd. Obsah můžeš vypsat příkazem dir C:\.

Cesty

Většina ze toho, co vypsal příkaz ls / jsou adresáře, kteŕ obsahují součásti a nastavení systému – třeba v bin jsou programy, které se dají spustit, v etc je nastavení, proc obsahuje informace o běžících procesech, v tmp jsou dočasné soubory, home obsahuje domovské adresáře uživatelů. Chceme-li se podívat, co obsahuje některý z těchto adresářů, dáme před jeho jméno lomítko, a použijeme opět příkaz ls. Třeba na alenčině notebooku obsahuje adresář /home jen jediný soubor – podadresář alenka:
$ ls /home
alenka
Na Windows musíš dát před jméno adresáře celé jméno disku, oddělené zpětným lomítkem. Třeba dir C:\Documents and Settings.
Pokud budu chtít pojmenovat adresář alenka, abych ho mohla vypsat pomocí ls, použiju jeho umístění (adresář, který ho obsahuje, také zvaný rodičovský adresář, angl. parent directory – v našem případě /home), a oddělím ho lomítkem od jména samotného adresáře:
$ ls /home/alenka
Desktop Documents  Downloads  Music  Pictures pyladies
Public Templates Videos
A stejně pro jakýkoliv jiný adresář:
$ ls /home/alenka/pyladies
01 02 03 04 05 venv
$ ls /home/alenka/pyladies/02
ahoj.py  ctverec.py  if.py  printing.py  rady.py
Na Windows to funguje stejně, jen se adresáře oddělují zpětným lomítkem.
Takové lomítky oddělené jméno adresáře se nazývá cesta (angl. path), a slouží
Cesta může vést i k normálnímu souboru. Příkaz ls tu vypíše jméno souboru, ne jeho obsah:
$ ls /home/alenka/pyladies/02/ahoj.py
/home/alenka/pyladies/02/ahoj.py
Pro vypsání obsahu by se místo příkazu ls použil cat, ve Windows pak místo dir příkaz type.
Použít normální soubor jako rodičovský adresář nejde: cesta jako /home/alenka/pyladies/02/ahoj.py/neco nikam nevede.

Relativní cesty

Zatím jsme používaly cesty, které začínají lomítkem (nebo, na Windows, jménem disku). Co se stane, když lomítkem nezačneme?
Každá příkazová řádka má aktuální adresář (angl. current working directory), který se – jak jsme si ukázaly na prvním srazu – dá vypsat příkazem pwd a změnit příkazem cd. (Na Windows se příkazem cd i vypisuje.)
Pokud zadám cestu bez počátečního lomítka, tak místo v kořenovém adresáři / taková cesta začíná tak, kde „právě jsem”. Je to cesta relativní (angl. relative) vzhledem k aktuálnímu adresáři.
$ pwd
/home/alenka
$ ls /home/alenka/pyladies/02
ahoj.py  ctverec.py  if.py  printing.py  rady.py
$ ls pyladies/02
ahoj.py  ctverec.py  if.py  printing.py  rady.py
Cestám, které začínají lomítkem (či jménem disku), se říká absolutní (angl. absolute).
Tohle platí i ve Windows. Pokud začíná jméno cesty jménem disku, jde o absolutní cestu na daném disku. Když začíná zpětným lomítkem (bez jména disku), rozumí se aktuální disk;

Rodičovský adresář

V každém adresáři existují dva speciální adresáře: rodičovský adresář, který se označuje dvěma tečkami (..), a aktuální adresář, který se označuje jednou tečkou (.):
$ pwd
/home/alenka
$ ls .
Desktop Documents  Downloads  Music  Pictures pyladies
Public Templates Videos
$ ls ..
alenka
Tyto adresáře se dají používat v cestách; například, pokud jsem v adresáři /home/alenka, tak cesta ../alenka/. znamená:
  1. ..: nahoru do /home,
  2. alenka: zpátky do /home/alenka,
  3. .: zůstat v /home/alenka.
Jde tedy o ten samý adresář.
Ve Windows .. a . znamenají přesně to samé jako v Unixu, jen se jména adresářů oddělují zpětnými lomítky.

Příkazy

A co je vlastně ono ls, kterým vypisujeme obsah adresáře?
Příkaz ls je program: speciální soubor, který se dá spustit, čímž vznikne proces který se tím programem řídí. Absolutní cesta k němu je /bin/ls. Jako programy v Pythonu, i /bin/ls obsahuje příkazu pro počítač, které se provádějí jeden po druhém. (Na rozdíl od Pythonu tyto příkazy nejsou čitelné – jsou v jazyce, kterému rozumí jen počítač. Máš-li Linux, otevři si /bin/ls v editoru a přesvědč se sama.)
Když zadáme do příkazové řádky ls, najde se soubor ls v adresáři /bin, a spustí se (vytvoří se z něj proces). Případné argumenty (např. pokud zadáme ls /bin, tak bin) se „předají” novému procesu, aby s nimi mohl pracovat.
Na Windows je situace trošku jiná, příkaz dir není uložen ve zvláštním souboru. Ale například Poznámkový blok (Notepad) funguje stejně – příslušný soubor bývá v C:\Windows\System32\notepad.exe, a spouští se zadáním notepad.

Proměnné

A jak příkazová řádka ví, že se příkazy mají hledat zrovna v /bin? Existuje na to proměnná – ano, i příkazová řádka má proměnné, i když fungují trochu jinak než v Pythonu. Dají se nastavit rovnítkem (pozor, kolem rovnítka tu nesmí být mezery), a vypsat příkazem echo, kde proměnnou uvedeme s dolarem na začátku:
$ promenna=xyz
$ echo $promenna
xyz
Na Windows zkus pro nastavení set promenna=xyz a pro vypsání echo %promenna%.
Proměnnou s dolarem můžeme použít v kterémkoli příkazu; nahradí se obsahem proměnné. Můžeme tedy napsat třeba:
$ adresar=/home/alenka/pyladies
$ echo $adresar
/home/alenka/pyladies
$ ls $adresar
Desktop Documents  Downloads  Music  Pictures pyladies
Public Templates Videos
nebo dokonce:
$ adresar=/home/alenka/pyladies
$ prikaz=ls
$ $prikaz $adresar
Desktop Documents  Downloads  Music  Pictures pyladies
Public Templates Videos

$PATH

Proměnná PATH obsahuje všechny cesty, ve kterých se mají hledat příkazy. Jsou oddělené dvojtečkou:
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/local/sbin:/usr/sbin:/home/petr/.local/bin:/home/petr/bin
Příkazová řádka zkouší všechny tyto adresáře, jeden po druhém, dokud nenajde zadaný příkaz – a první který najde, spustí. Naše PATH obsahuje i /bin, takže se najde příkaz /bin/ls.
Na Windows je proměnná PATH, která funguje stejně – zkus echo %PATH%.
Když proměnnou PATH změníme, příkazová řádka začne hledat příkazy jinde – a nemusí je vůbec najít!
$ PATH=/nejaka/neexistujici/cesta
$ ls
bash: ls: command not found...
Na Windows jde PATH nastavit pomocí set PATH=xyz, ale protože je tu víc příkazů zabudovaných (ne v souborech), „nerozbije se to” tak moc, jako v Unixu.
Pořád se ale dá zadat absolutní cesta k příkazu:
$ /bin/ls
Desktop Documents  Downloads  Music  Pictures pyladies
Public Templates Videos
Nebo se dá zadat i cesta relativní. Pokud jsme v adresáři /home/alenka a chceme soubor /bin/ls, chceme jít po této cestě:
  1. .. – nahoru do /home
  2. .. – nahoru do /
  3. bin – do /bin
  4. ls – k /bin/ls
$ ../../bin/ls
Desktop Documents  Downloads  Music  Pictures pyladies
Public Templates Videos
A nebo se můžeme přepnout do adresáře /bin, a spustit ls z aktuálního adresáře. Aktuální adresář ale musíme pojmenovat přímo, pomocí ., jinak se příkaz hledal v $PATH.
$ cd /bin  # příkaz cd je zabudovaný v příkazové řádce; nehledá se v PATH
$ ./ls
Desktop Documents  Downloads  Music  Pictures pyladies
Public Templates Videos
Po tomhle experimentu je dobré si otevřít novou příkazovou řádku, ve které bude PATH nastaveno správně.

Virtuální prostředí

A teď už víme dost na to, abychom si vysvětlily, jak funguje virtuální prostředí.
Virtuální prostředí je adresář. (To naše prostředí je adresář pojmenovaný venv.) V tomto adresáři je soubor s programem python, který se dá spustit, a spousta dalších souborů, které jsou pro fungování Pythonu potřeba. Zkus si program python ve svém virtuálním prostředí najít!
python z virtuálního prostředí ale není na žádné z cest, které jsou normálně v PATH, takže se normálně nespouští – když do čerstvě puštěné příkazové řádky napíšu python, spustí se systémový Python/usr/bin/python – ne ten z virtuálního prostředí. Na většině dnešních unixových systémů má /usr/bin/python verzi 2.6 nebo 2.7, což se nám nehodí.
Ve virtuálním prostředí je ještě jeden program, nazvaný activate. Na Unixových systémech to není samostatný program, ale tzv. skript pro program source – podobně jako program v Pythonu se nespouští přímo, ale např. python mujprogram.py.
Virtuální prostředí se aktivuje příkazem source ~/pyladies/venv/bin/activate. Je možné zadat i relativní cestu od aktuálního adresáře – to může být rychlejší, pokud „jsme” ve správném adresáři. Tento příkaz activate nastaví $PATH (a jiné proměnné) tak, aby se odteď po zadání python spouštěl onen Python z virtuálního prostředí.
Jedna z dalších proměnných, které activate nastaví, se jmenuje PS1 a obsahuje výzvu příkazové řádky (která většinou končí $; na Windows >). activate na začátek přilepí řetězec (venv), takže jednoduše poznáme, že jsme v našem virtuálním prostředí.
$ python --version
Python 2.7.8
$ source /home/alenka/pyladies/venv/bin/activate
(venv)$ python --version
Python 3.4.0
(venv)$ 
Na Windows jsme „systémový” Python nainstalovaly my, takže i ten má verzi 3.4, jak potřebujeme – jen tu ani ten systémový není standardně v PATH. Pokud sis nastavila PATH tak, aby náš Python obsahovala (což některé z vás udělaly), nebylo doteď až tak úplně potřeba virtuální prostředí spouštět. To se ale ode dneška změní, protože si začneme „naše” prostředí měni doinstalováváním dalších balíčků. Dávej tedy pozor, abys virtuální prostředí měla zapnuté, když je potřeba.

Bonus: Tilda

Unixové příkazové řádky mají ještě jeden způsob, jak zkrátit zápis: pokud cesta začíná vlnovkou (~), dosadí se za vlnovku domovský adresář aktuálního uživatele. Lze tedy psát:
$ ls ~/pyladies/02
ahoj.py  ctverec.py  if.py  printing.py  rady.py
Pokud tedy máš adresář pyladies přímo v domovském adresáři, tak ~/pyladies je absolutní cesta k němu – jen zkráceně zapsaná.
Funguje to ale jen v příkazové řádce. Až budeme se soubory pracovat z Pythonu, ~ se takhle chovat nebude – musíme zavolat speciální funkci, která vlnovku „rozbalí”.
$ python
>>> from os.path import expanduser
>>> print(expanduser('~/pyladies'))
/home/alenka/pyladies

Bonus: Vlastní příkazy

Je možné psát si vlastní příkazy pro příkazovou řádku. Pokud máš Linux (nebo macOS) a věříš si, můžeš to zkusit. Je to ale tak trochu nad rámec PyLadies :)
Do cest, které jsou normálně v $PATH většinou nemůže normální uživatel zapisovat, proto si vytvoř adresář ~/bin (ted adresář bin v domovském adresáři. Pokud ho ještě nemáš v PATH (to záleží na systému), můžeš ho přidat příkazem
PATH=/home/tvojejmeno:$PATH
tedy například
PATH=/home/alenka:$PATH
Funguje to jako v Pythonu a = a + 1: napřed to co v PATH bylo předtím, pak oddělovač, a pak nově přidaná cesta.
Potom vytvoř soubor ~/bin/kostka, a napiš do něj:
#! /cesta/k/virtualnimu/prostredi/bin/python
from random import randrange

print(randrange(1, 7))
První řádek tedy bude např.
#! /home/alenka/pyladies/venv/bin/python
Ověř si, že tvůj soubor /cesta/k/virtualnimu/prostredi/bin/python existuje!
Potom je potřeba tenhle soubor označit za spustitelný příkazem chmod:
chmod +x ~/bin/kostka
A teď se dá kostka použít jako příkaz!
$ kostka
5
$ kostka
2
$ kostka
3
Může ale nastat malý problém: když otevřeš novou konzoli, PATH tam už nemusí obsahovat ~/bin. Existuje soubor ~/.bashrc, který obsahuje příkazy, které se provedou vždycky když zapneš konzoli. Na konec tohoto souboru můžeš dát příkaz, který PATH nastaví podle tvých představ.