V dnešním příspěvku se podíváme na jednu záludnost, která vás může potkat při skriptování v Bashi či shellu obecně. Týká se odstranění prázdných řádků z konce souboru při použití tzv. command substitution, zapsaným jako `command`
či $(command)
.
O co jde?
Mějme soubor test.txt
, který obsahuje následující data:
test
Čili nejdříve jsou v něm dva prázdné řádky, potom řetězec test
a následně další dva prázdné řádky. Po provedení
$ cat test.txt
dostaneme očekávaný výstup
test
Pokud si ovšem výstup uložíme do proměnné a tu následně vypíšeme:
content="`cat test.txt`" echo "$content"
tak dostaneme pouze
test
Zmizely nám prázdné řádky na konci souboru!
Poznámka pro úplnost: Ekvivalentní zápis výše uvedeného je tento:
content="$(cat test.txt)" echo -n "$content"
Je to jen jiná syntaxe toho stejného: tzv. command substitution.
V čem je problém?
Jak uložení výstupu z cat
, tak vypsání obsahu proměnné je uzavřeno v uvozovkách. To by mělo být dostatečné pro zachování obsahu souboru. Bohužel, není tomu tak. Podle dokumentace totiž ona substituce provádí následující (zvýraznění jsem přidal já):
Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.
Neboli to, že došlo k odstranění prázdných řádků z konce souboru, je vlastnost (feature), nikoliv bug. Pokud by vás zajímala motivace pro toto chování, zkuste mrknout zde, kde je sepsáno pár domněnek. Osobně bych chápal, pokud by to odstraňovalo poslední prázdný řádek, ale takto moc ne. Možná je ale mé nepochopení způsobené frustrací z času stráveného hledáním, v čem je problém při psaní skriptu, ve kterém bylo potřeba zachovat i ty prázdné řádky na konci souboru...
Poznámka: netýká se to jen Bashe, ale shellu obecně.
Co s tím?
V tomto příspěvku jsou zmíněny tři způsoby, jak se podobnému chování vyhnout. Asi nejjednodušší mi přijde si za konec výstupu dát znak navíc, který se hned následně musí manuálně odstranit:
content="`cat test.txt; echo -n "x"`"; content=${content%x}
Pokud toto potřebujete na více místech, tak bude dobré si na to napsat funkci.