Portál AbcLinuxu, 21. května 2025 10:44

Unixové nástroje – 15 (basename/dirname, test, read)

9. 7. 2010 | David Watzke
Články - Unixové nástroje – 15 (basename/dirname, test, read)  

V tomto díle si povíme něco o příkazech často používaných v shellových skriptech: basename/dirname, testread.

Obsah

basename a dirname

link

Tyto dva příkazy slouží k získání konkrétní části cesty k souboru. Oběma příkazům se zadává kompletní cesta k souboru či adresáři. Příkaz basename slouží k výpisu samotného názvu souboru či adresáře, který získá ze zadané cesty, zatímco příkaz dirname vypíše název nadřazeného adresáře.

$ basename /tmp/soubor.txt
soubor.txt

$ dirname /tmp/soubor.txt
/tmp

Toto se hodí v různých skriptech, které přímo pracují se soubory. V různých shellech lze tyto externí příkazy nahradit za (podstatně) rychlejší vestavěné funkce, například v Bashi lze použít toto:

$ cesta="/home/dave/file.ogg"
$ adresar="${cesta%/*}"
$ soubor="${cesta##*/}"
$ echo -e "cesta: $cesta\nadresar: $adresar\nsoubor: $soubor"
cesta: /home/dave/file.ogg
adresar: /home/dave
soubor: file.ogg

Ovšem zde je důležité podotknout, že chování těchto dvou metod není úplně stejné. Můžete si to zkusit například na tomto příkladu:

$ cesta="/tmp/xyz/"
$ basename "$cesta"
xyz
$ echo "${cesta##*/}
# nevypíše nic

Proč bashová metoda nevypíše nic? Protože ${cesta##*/} znamená odstranění části řetězce z proměnné $cesta od začátku až po poslední lomítko (včetně).

Příkaz basename má ještě jednu vlastnost. Zadáte-li jako druhý argument příponu, tak ji z názvu souboru odstraní. Tedy za předpokladu, že by to neznamenalo výpis prázdného řetězce a že byla daná přípona v názvu nalezena. Není-li daná přípona v názvu nalezena, není to považované za chybu.

$ basename "/tmp/velky_soubor.tar.xz" .xz
velky_soubor.tar

test

link

Příkaz test slouží k vyhodnocování různých výrazů a tvrzení. Použijete-li příkaz správně, tak vždy pouze vrátí návratovou hodnotu 0, nebo 1 jako signalizaci, že tvrzení platí, nebo neplatí, a nic nevypíše.

test dovede pracovat se soubory a adresáři. Umí ověřit, zda je soubor či adresář zapisovatelný (přepínač -w), spustitelný (-x), zda lze číst (-r) či zda vůbec existuje (-e). Dále umí zjistit, zda je soubor obyčejný soubor (-f), blokové zařízení (-b), znakové zařízení (-c), symbolický odkaz (-h nebo -L), pojmenovaná roura FIFO (-p), socket (-S) či adresář (-d).

# ověřuje, zda cesta „/“ ukazuje na adresář;
# vrátí 0 (pravda), protože kořenový adresář existuje vždy
test -d /

# ověřuje, zda existuje soubor /tmp/xyz
test -e /tmp/xyz

Pomocí test lze také zkoumat řetězce znaků.

# pravda, pokud je řetězec $X nenulový
test "$X"

# pravda, pokud jsou řetězce $A a $B stejné
test "$A" = "$B"

# pravda, pokud jsou řetězce navzájem různé
test "$B" != "$C"

# pravda, pokud je řetězec nulové délky;
# toto vrátí 0
test -z ""

Dále lze porovnávat celá čísla. Je možné použít operátory:

# pravda, 150 je menší nebo rovno 150
test 150 -le 150

# nepravda, 100 = 100
test 100 -ne 100

Výrazy lze kombinovat pomocí přepínačů -a (logický AND, tedy „a zároveň“) nebo -o (logický OR, „nebo“). Dále je lze negovat přidáním vykřičníku před výraz. Můžeme také ovlivňovat prioritu vyhodnocování výrazů pomocí závorek.

# vrátí 1, protože minimálně druhý výraz neplatí (s -a musí platit oba)
test \( -w /tmp \) -a \( 100 -eq 101 \)

# pokud je adresář /tmp zapisovatelný, vrátí 0, protože platí alespoň jeden výraz
test \( -w /tmp \) -o \( 100 -eq 101 \)

# pravda, pokud soubor není zapisovatelný
test ! -w "soubor"

V praxi se test používá v shellových skriptech, nejčastěji v kombinaci s podmínkou if. Také se často místo test píše pouze hranatá závorka [, přičemž je to totéž, ale takto zapsaný příkaz je nutné ukončit opačnou hranatou závorkou ].

# toto:
test -x skript.sh
# je totéž jako toto:
[ -x skript.sh ]

Předvedeme si ukázkové použití v Bashi. Píšeme funkci my_mkdir, které zadáte název adresáře a ona jej vytvoří, pokud neexistuje. Ovšem nejdříve ověří, zda již neexistuje soubor či adresář stejného názvu a zařídí se podle toho (vypíše patřičnou informaci). Pomineme, že taková funkce nemá valný význam, protože mkdir podobné kontroly dělá sám.

my_mkdir() {
	# pokud soubor či adresář s tímto názvem již existuje
	if [ -e "$1" ]; then
		# pokud adresář s tímto názvem již existuje
		if [ -d "$1" ]; then
			echo "Adresář s tímto názvem již existuje."
			return 0
		else
			echo "Již existuje soubor se stejným názvem." >&2
			return 1
		fi
	else
		# vytvoříme adresář, protože soubor ani adresář s daným názvem neexistuje
		if mkdir -p "$1"; then
			echo "Adresář byl úspěšně vytvořen."
			return 0
		else
			echo "Adresář se nepodařilo vytvořit." >&2
			return 1
		fi
	fi
}

read

link

read je šikovný nástroj, který slouží k načtení jednoho řádku ze standardního vstupu do dané proměnné (či více proměnných).

Když vyžadujeme od uživatele nějaký vstup za běhu programu, můžeme read použít následovně:

echo -n "Jak se jmenuješ? "
read NAME
echo "Právě jsi mi prozradil, že se jmenuješ ${NAME}."

read má jediný přepínač, a sice -r, který slouží k nastavení toho, jak se má nakládat s výskytem znaku „\“ (zpětné lomítko). Bez něj je považován za escapující znak, přičemž s ním je brán doslova jako znak „\“.

Často se ve skriptech používá ke zpracování textového souboru řádek po řádku, pomocí cyklu while. Následuje ukázka v Bashi, která očísluje řádky daného souboru.

x="$IFS"
IFS=""
n=0
while read -r line; do
	echo "$((++n)) $line"
done < soubor.txt
IFS="$x"

Je možné načítat i do více proměnných najednou:

$ read a b
acko becko
$ echo $b :: $a
becko :: acko

Znak, podle kterého se daný řetězec rozdělí na několik částí, je dán proměnnou IFS, jak je naznačeno výše. Výchozí hodnota této proměnné jsou speciální znaky tab, mezera a nový řádek. Pokud je proměnná IFS nastavená na prázdnou hodnotu (pomocí IFS=""), k žádnému rozdělení nedojde.

Seriál Unixové nástroje (dílů: 27)

První díl: Unixové nástroje – 1 (úvod, cat, head, tail), poslední díl: Unixové nástroje – 26 (triky pro práci v Bashi).
Předchozí díl: Unixové nástroje – 14 (find)
Následující díl: Unixové nástroje – 16 (getopts, time, nohup)

Související články

Seriál: BASH
Seriál: Nebojíme se kompilace
Regulární výrazy
Bash: chytré doplňování
Barevný a formátovaný text v terminálu

Odkazy a zdroje

man dirname
man basename
man test
man read

Další články z této rubriky

VDR a DVB-T2, část 2.
VDR a DVB-T2, část 1.
Šifrovaný Proxmox VE 6: ZFS, LUKS, systemd_boot a Dropbear
MapTiler – proměňte obrázek v zoomovatelnou mapu
Syncthing

Diskuse k tomuto článku

9.7.2010 00:44 Pavel
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Odpovědět | Sbalit | Link | Blokovat | Admin
Me oblibene...
 cat neco | while read line; ...
9.7.2010 02:05 Ash | skóre: 53
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Ta varianta uvedená v článku má vůči této jisté objektivní výhody.
9.7.2010 09:13 vlk
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Radsej takto :
    <neco while read line; ...
9.7.2010 10:18 Pavel
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Dost casto potrebuju mezi tim catem a while neco grepovat, takze zobak je hezky, ale malo platny...
9.7.2010 12:30 Mti. | skóre: 31 | blog: Mti
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Oboji ma svoje. Ta roura Ti zase z toho while nepusti ven promenne... alespon ne naprimo.
Vidim harddisk mrzuty, jehoz hlava plotny se dotyka...
9.7.2010 12:51 Ash | skóre: 53
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
V Bashi lze i <(grep foo soubor.txt) což spojuje oboje výhody do jednoho. Ale to jen doplňuji, nic proti cat sem | tam.
10.7.2010 04:10 zulu
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
A to takhle nemůžeš?
10.7.2010 04:12 zulu
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Beru zpět, špatně jsem si to vyložil.
10.7.2010 04:07 zulu
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
A to funguje kde?
10.7.2010 23:41 plk
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
co kde kdy jak proc?
11.7.2010 04:55 zulu
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
<neco while
11.7.2010 14:26 Ash | skóre: 53
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Předpokládám že v sh, bash, zsh, csh, tcsh, máte nějaké podezření že to někde nefunguje?
11.7.2010 20:01 Jirka P
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Takhle to nefunguje nikde, ono totiž aby shell poznal (podle gramatiky) cyklus, musí být to rezervované slovo první. Nefunguje to minimálně v bashi a v dashi (co jsem teď vyzkoušel).

Podle posixové gramatiky navíc

<neco while x

znamená spuštění příkazu "while" (existuje-li někde na disku) s parametrem x a vstupem přesměrovaným z neco.
11.7.2010 20:56 Ash | skóre: 53
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
No jak jsem říkal, ta varianta popisovaná v článku má své výhody. Máte pravdu, nevím kam na tohle vlk šel, možná v tom zsh. Osobně používám to co je v článku.
9.7.2010 02:03 Ash | skóre: 53
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Odpovědět | Sbalit | Link | Blokovat | Admin
read má jediný přepínač, a sice -r

Běžné jsou i ready (asi ne POSIX?) co mají ještě -p.
read -p "Jak se jmenuješ? " NAME
13.7.2010 17:41 pht | skóre: 48 | blog: pht
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
read [-ers] [-u fd] [-t timeout] [-a aname] [-p prompt] [-n nchars] [-d delim] [name ...]
(bash)
In Ada the typical infinite loop would normally be terminated by detonation.
13.7.2010 20:24 Ash | skóre: 53
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Dobré :)
13.7.2010 17:43 pht | skóre: 48 | blog: pht
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Odpovědět | Sbalit | Link | Blokovat | Admin
K basename a dirname bych dodal ještě užitečný příkaz readlink.
In Ada the typical infinite loop would normally be terminated by detonation.
LennyCZ avatar 22.7.2010 00:06 LennyCZ | skóre: 8 | Brno
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Odpovědět | Sbalit | Link | Blokovat | Admin
Jak je prosím možné toto (jde o bash)?
lennycz@hugo:~$ IFS=x
lennycz@hugo:~$ read a b
aaaxbbbxcccxddd
lennycz@hugo:~$ echo $a
aaa
lennycz@hugo:~$ echo $b
bbb ccc ddd
lennycz@hugo:~$ 
Čekal bych jedno z tohoto:
  1. $a bude aaa a $b bude bbb, zbytek se zahodí
  2. $a bude aaa a $b bude pole složené z bbb, ccc a ddd, pak by se ale výpis měl objevit jako bbbxcccxddd, protože $IFS je nastaveno na x
Pletu se? (To je řečnická otázka, evidentně ano :-D)

Jinak samozřejmě díky za skvělý seriál :D...
Given a choice between dancing pigs and security, users pick dancing pigs every time. (Dancing pigs problem)
22.7.2010 07:30 petr_p | skóre: 59 | blog: pb
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Bash má vestavěný příkaz help. Prostudujte si výstup příkazu help read. Píše se tam, že do poslední proměnné se uloží zbytek řetězce.
LennyCZ avatar 22.7.2010 16:09 LennyCZ | skóre: 8 | Brno
Rozbalit Rozbalit vše Re: Unixové nástroje – 15 (basename/dirname, test, read)
Díky za vysvětlení, o příkazu help jsem nevěděl.
Given a choice between dancing pigs and security, users pick dancing pigs every time. (Dancing pigs problem)

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.