Cvičení: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.

Nezapomeňte, že Čtení před cvičením je povinné a je z něj kvíz, který musíte vyplnit před cvičením.

Čtení před cvičením

Regulární výrazy (neboli regex – regular expression)

Už jsme zmínili, že systémy Unixové rodiny jsou založeny na textových souborech. Utility, které jsme zatím viděli, nám umožnili základní operace s nimi, ale žádné z nich nebyly opravdu mocné. Použitím regulárních výrazů to změníme.

Nebudeme zde řešit teoretické detaily – na to je předmět Automaty a gramatiky. My se na regulární výrazy budeme dívat jako na jednoduché nástroje pro hledání vzorů (patternů) v textu.

Například nás můžou zajímat:

  • řádky začínající datem a obsahující HTTP kód 404,
  • řádky obsahující náš login,
  • nebo řádky předcházející řádkům s platnými názvy souborů.

Přestože regulární výrazy jsou velmi silné, jejich použití se trochu komplikuje nepříjemným faktem, že různé nástroje používají trochu odlišnou syntaxi. Mějte to na paměti například při používání příkazů grep a sed. Knihovny pro práci s regulárními výrazy jsou taky k dispozici ve většině programovacích jazyků, ale opět pozor na rozdíly v jejich syntaxi.

Nejzákladnějším nástrojem na vyhledávání regulárními výrazy v souborech je grep. (Legenda říká, že g ve jméně znamená “globally”, re je regex a p print.) Spustíme-li grep regex soubor, vypíší se všechny řádky souboru, které odpovídají zadanému regulárnímu výrazu. Během cvičení si vyzkoušíme spoustu příkladů.

Syntaxe regexů

V tom nejjednodušším tvaru regex hledá zadaný řetězec (obvykle s ohledem na velikost písmen).

system

Tímto zachytíme všechny podřetězce system v textu. Při použití grepu to znamená, že všechny řádky obsahující system se vypíší.

Chceme-li hledat řádky začínající tímto slovem, musíme přidat ukotvení ^.

^system

Má-li řádek končit zadaným vzorem, potřebujeme použít ukotvení $. V shellu je bezpečnější kolem vzoru používat jednoduché uvozovky, abychom se vyvarovali expanzi proměnných, atp.

system$

Také můžeme najít všechny řádky začínající jedním z písmen r, s, nebo t pomocí seznamu [...].

^[rst]

Vypadá to trochu jako wildcard, ale regexy jsou mnohem silnější a syntaxe se trochu liší.

Zkusme najít všechna třímístná čísla:

[0-9][0-9][0-9]

Tomuto budou vyhovovat jak všechna třímístná čísla, tak třeba i čtyřmístná: regulární výrazy bez ukotvení se vůbec nestarají o okolní znaky.

Můžeme také hledat řádky nezačínající žádným ze znaků mezi r a z. (První ^ je ukotvení, zatímco druhá neguje množinu v [].)

^[^r-z]

Kvantifikátor * značí, že předchozí část regexu se může vyskytovat vícekrát nebo třeba vůbec. Např. tímto najdeme všechny řádky, které obsahují jen číslice:

^[0-9]*$

Poznamenejme, že to nevyžaduje, aby všechny číslice byly stejné.

Tečka . odpovídá libovolnému jednomu znaku (kromě zalomení řádku). Následující regex pak zachytí řádky začínající super a končící ious:

^super.*ious$

Abychom mohli aplikovat * na složitější podvýraz, můžeme jej obalit (...). Následující regex zachytí bana, banana, bananana, atd.:

ba(na)*na

Použijeme-li + místo *, je vyžadován aspoň jeden výskyt. Tímto tak zachytíme všechna dekadická čísla:

[0-9]+

Svislá čára (|, příp. pipa) může oddělovat alternativy. Např. můžeme zachytit řádky složené z Meow a Quork:

^(Meow|Quork)*$

Konstrukce [abc] je tak jen zkratkou pro (a|b|c).

Další užitečnou zkratkou je kvantifikátor {N}: ten říká, že předchozí regex se musí opakovat N krát. Můžeme také použít {N,M} pro rozsah. Např. můžeme zachytit řádky obsahující 4–10 malých písmen uzavřených v uvozovkách:

^"[a-z]{4,10}"$

Nakonec je tu zpětné lomítko, které mění to, jestli je následující znak považovaný za speciální. \. zachytí opravdu tečku a \* hvězdičku. Naproti tomu mnoho dialektů regexů (vč. grepu bez dalších přepínačů) vyžaduje +, (, | a { odescapované, aby byly rozpoznány jako regexové operátory. (Můžete spustit grep -E nebo egrep pro aktivování rozšířených regulárních výrazů, které všechny speciální znaky rozpoznávají jako operátory bez zpětných lomítek.)

Nahrazování v textu

Plná síla regulárních výrazů se ukáže, když je použijeme na nahrazování vzorů. Ukážeme si to na příkazu sed (stream editor), který umí provádět textové transformace založené na regulárních výrazech.

V nejjednodušší podobě, sed nahrazuje jedno slovo jiným. Příkaz přečte nahrazení (s, substitute), pak jednoznakový oddělovač následovaný textem k nahrazení (levá strana substituce), opět stejný oddělovač, pak náhrada (pravá strana) a nakonec opět oddělovač. (Oddělovač je obvykle :, /, nebo #, ale obecně to může být libovolný znak, který se nepoužívá ve zbytku příkazu bez escapování.)

sed 's:magna:angam:' lorem.txt

Poznamenejme, že tímto nahradíme vždy jen první výskyt na každém řádku. Přidání modifikátoru g (global) na konec příkazu způsobí nahrazení všech výskytů:

sed 's:magna:angam:g' lorem.txt

Text, který má být nahrazen, může být libovolný regulární výraz, např.:

sed 's:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]:DATE-REDACTED-OUT:g' lorem.txt

Pravá strana se může odkazovat na text zachycený levou stranou. Můžeme použít & pro celou levou stranu nebo \n pro n-tou skupinu (...) na levé straně.

Následující příklad převede datum do českého tvaru. Podobně jako u grepu musíme odescapovat znaky ( a ), aby fungovaly jako seskupující operátory namísto literálů ( a ).

sed 's:\([0-9][0-9][0-9][0-9]\)-\([0-9][0-9]\)-\([0-9][0-9]\):\3. \2. \1:g'

Další příklady si ukážeme během cvičení, zůstaňte s námi.

ShellCheck

Už jste si napsali celkem dost shellových skriptů. Je proto čas představit ShellCheck. ShellCheck je nástroj, který kontroluje shellové skripty a hledá v nich známé problémy. Nejde o chyby syntaxe ani logické chyby. Jde o odhalení konstrukcí, u nichž je známo, že často způsobují neočekávané chování, snižují výkon, nebo skrývají nějaká nepříjemná překvapení.

Jedním takovým příkladem může být, když váš skript obsahuje následující kus kódu.

cat input.txt | cut -d: -f 3

Víte, co by mohlo být špatně? Technicky je tento kód správně a sám o sobě neobsahuje bug. Použití příkazu cat je ale redundantní, protože vypisuje jen jeden soubor: kód by se dal zjednodušit do následujícího tvaru beze změny funkčnosti:

cut -d: -f 3 <input.txt

Jak vidíte, v zásadě nejde o nic škodlivého. Může to ale znamenat, že jste chtěli spojit více souborů, nebo je cat jen pozůstatek předchozí verze. Tak vás ShellCheck varuje.

Jiný problém, kdy ShellCheck může pomoct je následující kód:

dir_name=results/
if [ -d $dri_name ]; then
    echo "$dir_name already exists."
fi

Tady ShellCheck detekuje překlep, protože do proměnné dri_name nebylo předem nic přiřazeno.

Další nástraha čeká v následujícím kódu:

if [ -d ]; then
    echo "$dir_name already exists."
fi

Tohle je samozřejmě úplně špatně. Ale hádejte: test (nebo [) to příjme a vyhodnotí jako true. Vypadá to zvláštně, ale test s přesně jedním argumentem ověřuje jestli je argument neprázdný. Dnes už máme test -n, ale dříve jsme neměli a musíme zachovat zpětnou kompatibilitu. Viz tato stránka pro podrobnosti.

Je to tedy korektní kus shellového kódu, ale nejspíš nedělá to, co jsme chtěli. Zde přichází ShellCheck, aby nám pomohl.

ShellCheck umí varovat před stovkami možných problémů, jak lze vidět na této stránce. Zvykněte si jej běžně spouštět na svých shellových skriptech.

Z naší zkušenosti ShellCheck dává zřídka falešně pozitivní výsledky, ale mnohokrát nás zachránil.

Některé z hodnocených úloh, které odevzdáte, budou kontrolovány i ShellCheckem (a můžeme penalizovat vaše řešení, pokud obsahuje ShellCheckové chyby).

Jiné lintery a checkery

Poznamenejme, že ShellCheck není jediným dostupným nástrojem. V podstatě každý programovací jazyk má podobné nástroje, příp. nástroje na kontrolu stylu.

Například Python má Pylint, který byste určitě měli vyzkoušet a pravidelně používat. Může objevit skutečné chyby, stejně jako udělat kód více Pythonní – to je důležité pro práci v týmu.

Co si odnést

Začněte používat ShellCheck, Pylint, případně libovolné další nástroje pro váš oblíbený jazyk.

Neodhalí se tím logické chyby (nebo aspoň ne všechny), ale určitě se odhalí tzv. code smells: místa v kódu, která často vedou k chybám, nedefinovanému chování, nebo jiným problémům.

Tohle je dvakrát tak důležité, učíte-li se nějaký nový jazyk: jsou tu šance, že jste si něco špatně vyložili namísto chyby v nástroji.

Kvíz před cvičením

Soubor s kvízem je ve složce 08 v tomto GitLabím projektu.

Zkopírujte si správnou jazykovou mutaci do vašeho projektu jako 08/before.md (tj. budete muset soubor přejmenovat).

Otázky i prostor pro odpovědi jsou v souboru, odpovědi vyplňte mezi značky **[A1]** a **[/A1]**.

Pipeline before-08 na GitLabu zkontroluje, že jste odevzdali odpovědi ve správném formátu. Ze zřejmých důvodů nemůže zkontrolovat skutečnou správnost.

Odevzdejte kvízy před začátkem osmého cvičení.

Testování v shellu pomocí frameworku BATS

In this section we will briefly describe BATS – the testing system that we use for automated tests that are run on every push to GitLab.

Generally, automated tests are the only reasonable way to ensure your software is not slowly rotting and decaying. Good tests will capture regressions, ensure bugs are not reappearing and often serve as documentation of the expected behavior.

The motto write tests first may often seem exaggerated and difficult, but it contains a lot of truth (several reasons are listed for example in this article).

BATS is a system written in shell that targets shell scripts or any programs with CLI interface. If you are familiar with other testing frameworks (e.g. Python Nose), you will find BATS probably very similar and easy to use.

Generally, every test case is one shell function and BATS offers several helper functions to structure your tests.

Let us look at the example from BATS homepage:

#!/usr/bin/env bats

@test "addition using bc" {
  result="$(echo 2+2 | bc)"
  [ "$result" -eq 4 ]
}

The @test "addition using bc" is a test definition. Internally, BATS translates this into a function (indeed, you can imagine it as running simple sed script over the input and piping it to sh) and the body is a normal shell code.

BATS uses set -e to terminate the code whenever any program terminates with non-zero exit code. Hence, if [ terminates with non-zero, the test fails.

Apart from this, there is nothing more about it in its basic form. Even with this basic knowledge, you can start using BATS to test your CLI programs.

Executing the tests is simple – make the file executable and run it. You can choose from several outputs and with -f you can filter which tests to run. Look at bats --help or here for more details.

Commented example

Let’s write a test for our factor.py program. We will use the version that reads the number from argv.

#!/usr/bin/env bats

@test "Factorize 7" {
    run ./factor.py 7
    [ "$output" = "7" ]
}

@test "Factorize 17" {
    run ./factor.py 17
    [ "$output" = "17" ]
}

We use a special BATS command run to execute our program that also captures its stdout into a variable named $output.

And then we simply verify the correctness.

Let’s add another test case:

@test "Factorize 8" {
    run ./factor.py 8
    [ "$output" = "2 2 2" ]
}

This will fail, but the error message is not very helpful.

   (in test file factor.bats, line 15)
     `[ "$output" = "2 2 2" ]' failed

This is because BATS is a very thin framework that basically checks only the exit codes and not much more.

But we can improve that.

#!/usr/bin/env bats

check_it() {
    run ./factor.py "$1"
    [ "$output" = "$2" ]
}

@test "Factorize 7" {
    check_it 7 7
}

@test "Factorize 17" {
    check_it 17 17
}

@test "Factorize 8" {
    check_it 8 "2 2 2"
}

The error message is not much better but the test is much more readable this way.

Let’s improve the check_it function a bit more.

check_it() {
    run ./factor.py "$1"
    if [ "$output" = "$2" ]; then
        return 0
    fi
    echo >&2
    echo "-- Actual output --" >&2
    echo "$output" >&2
    echo "-- Expected output --" >&2
    echo "$2" >&2
    return 1
}

Spusťte test znovu:

   (from function `check_it' in file factor.bats, line 13,
    in test file factor.bats, line 25)
     `check_it 8 "2 2 2"' failed

   -- Actual output --
   2
   2
   2
   -- Expected output --
   2 2 2

So basically our test was wrong all the time :-).

But this is actually usable for debugging our program.

We simply need to change our test a bit:

@test "Factorize 8" {
    check_it 8 "2
2
2"
}

Yes, shell strings can span multiple lines just fine.

Adding more test cases is now a piece of cake. After this trivial update, our test suite will actually start making sense. And it will be useful to us.

Lepší testování (assertions)

BATS offers extensions for writing more readable tests.

Thus, instead of calling test directly, we can use assert_equal that produces nicer message.

assert_equal "expected-value" "$actual"

Testy pro NSWI177

Our tests are packed with the assert extension plus several of our own. All of them are part of the repository that is downloaded by run_tests.sh in your repositories.

Feel free to execute the *.bats file directly if you want to run just certain test locally (i.e., not on GitLab).

grep and sed

We have already mentioned these commands. The first one prints lines matching a given regular expression, the other one is able to change the lines according to the provided regular expression and its replacement.

Warning: both commands use a slightly different regex syntax. Always check with the man page if you are not sure. Generally, the biggest differences across tools/languages are in handling of special characters for repetition or grouping ((), {}).

Cvičení

Find all lines in /etc/passwd that contain the digit 9.

Accounts with /sbin/nologin in /etc/passwd are generally system accounts not used by a human user. Print the list of these accounts. Solution.

Find all lines in /etc/passwd that start with any of the letters A, B, C or D (case-insensitive). Solution.

Find all lines which contain an even number of characters. Solution.

Find all e-mail addresses. Assume that a valid e-mail address has a format <s1>@<s2>.<s3>, where each sequence <sN> is a non-empty string of characters from English alphabet and sequences <s1> and <s2> may also contain digits or a dot .. Solution.

Print all lines containing a word (in English alphabet) which begins with capital letter and all other letters are lowercase. Test that the word TeX will not be matched. Solution.

Remove all trailing spaces and tabulators. Solution.

Put every word (non-empty sequence of characters of the English alphabet) in parentheses. Solution.

Replace “Name Surname” by “Surname, N.”. Solution.

Delete all empty lines. Hint. Solution.

Reformat input to contain each sentence on a separate line. Assume that each sentence begins with a capital English letter and ends with ., !, or ?; there may be any number of spaces between sentences. Hint. Solution.

Příklad většího skriptu

We will describe the following script in a bit more detail to explain typical idioms you can encounter. We will also build the script incrementally to give you an idea how to approach building bigger scripts.

But we provide complete script as well for you to check that you have build it from the fragments correctly.

Popis úlohy

Write a script that prints basic system information (hardware platform, kernel version, number of CPUs, and RAM size). The user should be able to choose different output formats.

Solution.

Popis řešení

The core of our script is simple.

echo "Hardware platform: $( uname -m )"
echo "Kernel version: $( uname -r )"
echo "CPU count: $( nproc )"
echo "RAM size: $( sed -n 's#^MemTotal:[ ]*\([0-9]*\) kB#\1#p' </proc/meminfo )"

This output is useful for a human reader but not for machine processing. So let’s add a version that prints the output as assignment to shell variables that can be later used. I.e., in the following format.

PLATFORM="x86_64"
KERNEL_VERSION="5.10.16-arch1-1"

Of course, duplicating the script to contain the following is not a nice solution.

if [ "$format" = "shell" ]; then
    echo "PLATFORM=$( uname -m )"
    ...
else
    echo "Hardware platform: $( uname -m )"
    ...
fi

But it is possible to convert between these two formats. Let’s convert our script like this:

if [ "$format" = "shell" ]; then
    column_no=1
else
    column_no=2
fi
(
    echo "PLATFORM:Hardware platform:$( uname -m )"
    echo "KERNEL_VERSION:Kernel version:$( uname -r )"
    echo "CPU_COUNT:CPU count:$( nproc )"
    echo "RAM_TOTAL:RAM size:$( sed -n 's#^MemTotal:[ ]*\([0-9]*\) kB#\1#p' </proc/meminfo )"
) | cut '-d:' -f $column_no,3-

Not perfect but we are getting there. Let’s hide the conversion into a separate shell function.

format_normal() {
    cut '-d:' -f 2,3
}

format_shell() {
    cut '-d:' -f 1,3 | sed 's#:\(.*\)#="\1"#'
}

Then the script would contain the following pipeline:

(
    ...
    echo "RAM_TOTAL:RAM size:$( sed -n 's#^MemTotal:[ ]*\([0-9]*\) kB#\1#p' </proc/meminfo )"
) | "format_${format}"

In a sense, we have used a polymorphism in our script as the $format variable is technically a replacement of a virtual method table.

Adding JSON is a bit more complicated, but still doable. Note that we down-case the variable names for nicer output. The final sed is used to replace the trailing comma (JSON is a very strict format).

format_json() {
    local varname
    local varvalue
    echo "{"
    cut '-d:' -f 1,3 | sed 's#:# #' | while read -r varname varvalue; do
        echo -n "$varname" | tr 'A-Z' 'a-z' | sed 's#.*#  "&": #'
        echo "\"$varvalue\"",
    done | sed '$s#,$##'
    echo "}"
}

We can certainly use getopt to allow the user to select the output format but we will opt for using a configuration file or setting an environment variable. Then, the default format can be specified in "$HOME/.nswi177/sysinfo.rc" or the script can be launched with:

FORMATTER=json ./sysinfo.sh

Many programs offer you all three options where the script first loads the settings from a configuration file, optionally overrides them with a environment variable, and getopt can override these.

The loading in the script then looks like this (we switched to capitals to emphasize that the variable comes from the user and thus will be exported).

if [ -r "$HOME/.nswi177/sysinfo.rc" ]; then
    . "$HOME/.nswi177/sysinfo.rc"
fi

if [ -z "${FORMATTER:-}" ]; then
    FORMATTER="${DEFAULT_FORMATTER:-normal}"
fi

Hodnocené úlohy (deadline: 17. dubna)

DŮLEŽITÉ UPOZORNĚNÍ #1: úlohy níže záměrně zjednodušují některé předpoklady a cílí na dobře naformátovaný vstup. Pokud není chování definováno v textu, je definováno testy. Mnoho případů není záměrně definováno ani testováno – použijte selský rozum pro specifikaci chování v těchto případech.

DŮLEŽITÉ UPOZORNĚNÍ #2: nezapomeňte si vaší implementaci zkontrolovat nástrojem ShellCheck.

08/timeconv.sh (20 bodů)

Napište skript, který převede čas ve formátu AM/PM do 24-hodinového.

Skript čte stdin a tiskne výsledky na stdout. Žádné argumenty nebudou předány a neočekává se, že by nějaké měly být rozpoznány.

Skript najde všechny výskyty hh:mmAM nebo hh:mmPM a nahradí je jejich ekvivalentem v 24-hodinovém formátu.

Příklad vstupu/výstup může vypadat takto:

The event starts at 03:25PM and is expected to end at 06:17PM.
Registration will be opened from 09:00AM until 06:00 PM.
The event starts at 15:25 and is expected to end at 18:17.
Registration will be opened from 09:00 until 06:00 PM.

Očekáváme, že použijete samostatné výrazy pro jednotlivé odpolední hodiny, protože převádět 03 na 15, 04 na 16 atd. přímo v sedu není úplně přímočaré.

Ale můžete zkusit části skriptu vygenerovat. Nápověda:

echo "49 50 51 52 53 54" | sed -e "$( for i in 50 51 52; do echo "s:$i:$(( i - 50 )):g"; done )"

08/ip.sh (20 bodů)

Stáhněte si zde výňatek ze záznamů o přístupech serveru Apache. V podstatě je to seznam souborů, o které byl webové server požádán (např. tak, že uživatel napsal jejich URL nebo klepl na odkaz). Tento soubor obsahuje jak úspěšné přístupy tak i záznamy, kde se požadavek nepodařilo vyřídit, protože soubor neexistoval (situace známá jako HTTP 404).

Některé položky jsou obyčejné překlepy, ale některé odhalují roboty, které se pokoušely proniknout do instalace WordPressu (která ovšem na serveru nikdy nebyla).

Každá řádka obsahuje IP adresu původce požadavku, datum, požadované URL (včetně metody), chybový kód, velikost odpovědi a identifikaci prohlížeče (user agent).

Váš skript bude takový soubor číst na stdinu a vypíše IP adresu stroje, který se nejvíce-krát pokoušel přistoupit na neexistující stránky (podívejte se po 404). Žádné argumenty nebudou předány a neočekává se, že by nějaké měly být rozpoznány.

Testy pracují na drobných částech původní souboru, abychom vám usnadnili ladění. Odkaz výše slouží jak ukázka skutečných dat.

Při skutečném nasazení byste použili speciální nástroje pro zpracování podobných dat. Ty by nabídly výsledky ve strukturovanějším formátu. Nicméně, grep a sed jsou perfektní nástroje pro hobby server nebo pokud pracujete v nějak omezeném prostředí.

IP adresy jsme náhodně upravili tak, abychom zachovali anonymitu.

Mimochodem, pro úplný log je nejhorší (anonymizovanou) IP adresou 62.150.128.144.

08/normalize.sh (20 bodů)

Napište skript, který znormalizuje danou cestu.

Skript očekává jediný argument: cestu, kterou má normalizovat. Můžete předpokládat, že argument bude vždy uveden.

Skript provede normalizaci cesty následujícím způsobem:

  • odkazy na aktuální adresář budou odstraněny, protože jsou nadbytečné
  • odkazy na nadřazený (rodičovský) adresář budou odstraněny takovým způsobem, aby se nezměnil význam cesty (možná i opakovaně)
  • skript nebude převádět relativní cestu na absolutní nebo naopak
  • skript nebude kontrolovat, zda-li soubor skutečně existuje

Následující příklady ilustrují očekávané chování.

  • /etc/passwd/etc/passwd
  • a/b/././c/da/b/c/d
  • /a/b/../c/a/c
  • /usr/../etc//etc/

Můžete předpokládat, že jednotlivé komponenty cesty neobsahují znak nového řádku nebo další speciální znaky jako :, ", : nebo nějakou escape sekvenci.

Nápověda: sed ':x; s/abb/ba/; tx' zajistí, že s/abb/ba/ je voláno opakovaně, dokud probíhá náhrada (:x definuje návěští a tx je podmíněný skok na návěští pokud předchozí nahrazování změnilo vstup). Vyzkoušejte s echo 'abbbb' | sed ....

08/markdown.sh (40 bodů)

Napište jednoduchý převaděč z Markdownu do HTML.

Opět záměrně (a hodně) zjednodušujeme syntaxi: plnohodnotný parser by tady zafungoval lépe, ale účelem úlohy je vyzkoušet si práci se základními regulárními výrazy.

Převaděč musí podporovat následující styly:

  • Text se _zdůrazněním několika slov_. se vytiskne jako Text se <em>zdůrazněním několika slov</em>.
  • Text se *silným zdůrazněním*. se převede na Text se <strong>silným zdůrazněním</strong>.
  • Libovolný znak z množiny >, < a & musí být převeden na HTML entitu.
  • Odkazy ve formátu [http://...|text odkazu] budou převedeny na <a href="http://...">text odkazu</a>.
    • URL bude vždy začínat na http:// nebo https://
    • Znaky <, >, & a " musí být escapovány uvnitř URL, tj. musí být převedeny na odpovídající HTML entity.

Skript bude ignorovat další obvyklé vlastnosti Markdownu jako detekce odstavců nebo seznamů (ať už číslovaných nebo odrážkových).

Nepožadujeme a nebudeme testovat funkci při vnoření výše zmíněných značek. Takže není potřeba ošetřovat situaci jako nějaké _zdůraznění *uvnitř* dalšího_ nebo _speciální > znaky_ atd.

Můžete také předpokládat, že formátovací znaky nejdou přes více řádků. Může jich být více na jednom řádku, ale nebudou se překrývat.

Skript čte stdin a tiskne výsledky na stdout. Žádné argumenty nebudou předány a neočekává se, že by nějaké měly být rozpoznány.

Učební výstupy

Znalosti konceptů

Znalost konceptů znamená, že rozumíte významu a kontextu daného tématu a jste schopni témata zasadit do většího rámce. Takže, jste schopni …

  • vysvětlit, co je to regulární výraz

  • vysvětlit, proč se lintery a style checkery mají používat na kontrolu zdrojových kódů

Praktické dovednosti

Praktické dovednosti se obvykle týkají použití daných programů pro vyřešení různých úloh. Takže, dokážete …

  • vytvářet a používat jednoduché regulární výrazy na filtrování textu grepem

  • používat sed na nahrazování v textu

  • používat . a source

  • používat a interpretovat výsledky Shellchecku

  • používat a interpretovat výsledky Pylintu

  • spouště testy založené na BATS

  • číst testy BATS

  • vytvářet jednoduché testy BATS (volitelné)