Zajímavosti z shellu: nežádoucí odstranění koncových prázdných řádků

Od Petr Zemek, 2013-12-01

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.

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
2 + 5 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.