Obsah
1. Využití funkce input() bez dalších rozšíření
2. Rozhraní mezi Pythonem a knihovnou GNU Readline
3. Ukázka základního použití knihovny readline
4. Jednoduché automatické doplňování příkazů
5. Vylepšení automatického doplňování příkazů
6. Použití parametru state v okamžiku většího množství dostupných příkazů
7. Konfigurace knihovny GNU Readline – soubor .inputrc
9. Instalace knihovny prompt_toolkit
10. Využití funkce prompt_toolkit.prompt
13. Automatické doplňování příkazů s využitím třídy WordCompleter
14. Režim emulující příkazy editorů Vi a Vim
15. Režim emulující příkazy editoru Emacs
16. Použití objektu PromptSession v režimu zadávání hesla
17. Standardní klávesové zkratky použité knihovnou GNU Readline v režimu Emacs
18. Standardní klávesové zkratky použité knihovnou GNU Readline v režimu Vi
19. Repositář s demonstračními příklady
1. Využití funkce input() bez dalších rozšíření
Popis možností Pythonu při čtení dat z příkazové řádky začneme tou nejzákladnější funkcí, která je součástí standardního modulu Pythonu (a není ji tedy zapotřebí importovat). Tato funkce se v Pythonu 3.x jmenuje input() a slouží k načtení řetězce ze standardního vstupu, ovšem s tím, že se z řetězce odstraní znak konce řádku. Této funkci je možné předat nepovinný řetězec, který bude zobrazen jako výzva (prompt) na terminálu:
>>> help("input") Help on built-in function input in module builtins: input(...) input([prompt]) -> string Read a string from standard input. The trailing newline is stripped. If the user hits EOF (Unix: Ctl-D, Windows: Ctl-Z+Return), raise EOFError. On Unix, GNU readline is used if enabled. The prompt string, if given, is printed without a trailing newline before reading.
V Pythonu 2.x je situace nepatrně složitější, protože stejnou činnost vykonává funkce nazvaná raw_input(). Funkce input(), která taktéž v Pythonu 2.x existuje, se navíc snaží řetězec zapsaný na standardní vstup zpracovat funkcí eval() (a tedy například naparsovat celé číslo, seznam atd.), což většinou není činnost, kterou v praxi vyžadujeme:
>>> help("input") input(...) input([prompt]) -> value Equivalent to eval(raw_input(prompt)).
V dalším textu budeme předpokládat použití Pythonu 3.x. Následující příklad je skutečně velmi jednoduchý a ukazuje, jakým způsobem lze použít funkci input() pro načtení příkazu (prozatím se tento příkaz pouze vytiskne zpět na terminál a dále s ním nebudeme pracovat):
cmd = input("Command: ") print(cmd)
Druhý příklad je nepatrně složitější, protože v něm vytváříme kostru „aplikace“, která postupně načítá příkazy ze svého příkazového řádku a nějakým způsobem na ně reaguje. Prvotní verze této aplikace pouze rozpoznává trojici příkazů „quit“, „exit“ a „eval“, popř. jejich zkrácené varianty. Zpracování příkazů je realizováno v nekonečné smyčce ukončení až zadáním příkazu „quit“, popř. jiným násilným ukončením aplikace (Ctrl+D atd.):
def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) while True: cmd = input("Command: ") if cmd in {"q", "quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
2. Rozhraní mezi Pythonem a knihovnou GNU Readline
Již v nápovědě vypsané v úvodní kapitole k funkci input() jsme si mohli přečíst zmínku o knihovně GNU Readline. Tuto knihovnu je totiž možné využít pro vylepšení vlastností funkce input(), především pak pro přidání těchto vlastností:
- Možnost použití kurzorových šipek pro pohyb na řádku se zapisovaným vstupním textem
- Příkazy do určité míry emulující editory Emacs či Vi (v Emacs režimu například Ctrl+A pro přechod na začátek řádku, Ctrl+W pro smazání slova atd.)
- Doplňování příkazů (vstupních dat), což je téma, které si popíšeme ve čtvrté kapitole
- Vyhledávání v historii již zadaných příkazů (vstupních dat). Tuto funkcionalitu je možné zajistit i externě s využitím knihovny rlwrap.
I o podpoře GNU Readline v Pythonu se dozvíme přímo z integrované nápovědy. Tentokrát se bude nápověda týkat modulu nazvaného readline. K nápovědě k tomuto modulu se lze dostat několika způsoby. První způsob využívá přechodu do režimu nápovědy zavoláním funkce help() z interaktivní smyčky REPL:
$ python3 Python 3.4.3 (default, Nov 28 2017, 16:41:13) [GCC 4.8.4] on linux Type "help", "copyright", "credits" or "license" for more information. >>> help() Welcome to Python 3.4's help utility! help> readline Help on module readline: NAME ... ... ...
Druhý způsob přímo vyvolá nápovědu k modulu, a to bez přechodu do režimu nápovědy:
>>> help("readline")
3. Ukázka základního použití knihovny readline
Aby došlo – a to prakticky zcela automaticky – k rozšíření možností funkce input() o většinu výše uvedených vlastností (plně editovatelný vstupní řádek, historie zadaných dat atd.), postačuje doplnit náš demonstrační příklad o jediný řádek:
import readline
V průběhu importu tohoto modulu dojde k inicializaci knihovny GNU Readline a po spuštění příkladu již bude možné použít kurzorové šipky a další dále popsané editační operace, historii již zadaných vstupních dat (příkazů) a vyhledávání v této historii. Žádné další úpravy v příkladu přitom není nutné provádět:
import readline def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) while True: cmd = input("Command: ") if cmd in {"q", "quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
Interně nyní bude funkce input() používat například historii dat zapsaných do vstupního řádku atd., takže bude možné využít kurzorových šipek pro průchod historií, vyhledání v historii atd.
4. Jednoduché automatické doplňování příkazů
Jedna z nejužitečnějších vlastností knihovny GNU Readline spočívá v možnosti naprogramování automatického doplňování příkazů (nebo jiných vstupních dat) po stisku klávesy Tab. Tuto funkci pravděpodobně znáte z shellu popř. přímo z REPLu Pythonu (do dokonalosti je dovedena v IPythonu). Technologie automatického doplňování je založena na volání callback funkce nazývané completer přímo z knihovny GNU Readline. Této funkci se předávají dva údaje – již zapsaný (částečný) text a celočíselná hodnota state, jejíž význam bude vysvětlen v navazujících kapitolách. Pokud completer vrátí nějaký text, bude si ho knihovna GNU Readline pamatovat jako jedno z možných slov pro automatické doplnění. Pokud naopak vrátí hodnotu None, znamená to, že další volání této callback funkce již nemá význam a bude ukončeno.
Podívejme se nyní na jednu (prozatím značně primitivní) implementaci completeru. Tato implementace bude pracovat následovně: pokud je hodnota state nastavena na hodnotu 0 (první volání completeru), bude vstupní text analyzován a pokud bude odpovídat začátku nějakého známého příkazu, vrátí se jeho plná podoba. Tj. například při vstupu „qu“ se vrátí text „quit“ atd. Při druhém volání completeru se již pouze vrátí hodnota None znamenající, že GNU Readline vždy zkompletuje celý příkaz a nikdy nenabídne více než jednu možnost (alternativní implementaci si uvedeme v dalším textu):
def completer(text, state): # print(text, state) if state >= 1: return None if text in {"q", "qu", "qui"}: return "quit" elif text in {"e", "ex", "exi"}: return "exit" elif text in {"h", "he", "hel"}: return "help" else: return text
Povolení doplňování příkazů a nastavení completeru se provede takto. První řádek nastaví completer, druhý řádek pro jistotu nastaví mapování klávesy Tab na operaci „complete“:
readline.set_completer(completer) readline.parse_and_bind("tab: complete")
Úplný zdrojový text příkladu, který podporuje automatické doplňování jmen tří příkazů, bude vypadat následovně:
import readline def completer(text, state): # print(text, state) if state >= 1: return None if text in {"q", "qu", "qui"}: return "quit" elif text in {"e", "ex", "exi"}: return "exit" elif text in {"h", "he", "hel"}: return "help" else: return text def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) readline.set_completer(completer) readline.parse_and_bind("tab: complete") while True: cmd = input("Command: ") if cmd in {"quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
5. Vylepšení automatického doplňování příkazů
Předchozí implementace completeru nebyla příliš efektivní, a to minimálně ze dvou důvodů. První důvod spočíval v tom, že bylo nutné postupně zadávat všechny možné zkrácené tvary příkazů a navíc byly tyto tvary přímo součástí těla příslušné callback funkce. Ovšem completer lze relativně snadno vylepšit. Nejdříve všechny dostupné příkazy (nikoli jejich zkrácené tvary!) uložíme do proměnné mimo vlastní callback funkci, takže je bude možné snadno modifikovat nebo načíst z konfiguračního souboru:
WORDS = ("quit", "exit", "eval", "help")
Dále vlastní implementaci completeru upravíme takovým způsobem, aby se na základě částečného textu zadaného uživatelem našel první vyhovující příkaz. Je to snadné – budeme procházet n-ticí WORDS s příkazy a pokud zadaný n-znakový text bude odpovídat prvním n znakům příkazu, vrátíme přímo úplný tvar příkazu. Případné nejednoznačnosti typu „e→exit“ a „e→eval“ jsou vyřešeny pořadím příkazů (vrátí se první nalezený):
def completer(text, state): # print(text, state) if state >= 1: return None n = len(text) for word in WORDS: if text == word[:n]: return word else: return text
Opět si ukažme plnou verzi zdrojového kódu tohoto demonstračního příkladu:
import readline WORDS = ("quit", "exit", "eval", "help") def completer(text, state): # print(text, state) if state >= 1: return None n = len(text) for word in WORDS: if text == word[:n]: return word else: return text def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) readline.set_completer(completer) readline.parse_and_bind("tab: complete") while True: cmd = input("Command: ") if cmd in {"quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
6. Použití parametru state v okamžiku většího množství dostupných příkazů
Předchozí implementace completeru trpěla poměrně vážným nedostatkem – pokud došlo při pokusu o doplnění příkazu k nejednoznačnosti (dva příkazy začínající na „e“), doplnil se první nalezený příkaz (v tomto případě „exit“) a uživatel se ani nedozvěděl o existenci příkazu alternativního. Toto chování je možné napravit, a to relativně snadno. Využijeme zde faktu, že parametr state se při každém volání pro stejný vstupní řetězec bude postupně zvyšovat o jedničku. Pokud jako návratovou hodnotu použijeme jiný řetězec, dojde k jeho zapamatování a následně knihovna GNU Readline zobrazí seznam všech dostupných alternativ formou „menu“. Naše úprava tedy bude spočívat v tom, že vrátíme n-tý příkaz odpovídající vstupu, samozřejmě za předpokladu, že takový příkaz vůbec existuje. Úprava není příliš efektivní, ovšem v našem příkladu se čtyřmi příkazy plně dostačující:
def completer(text, state): # print(text, state) matches = [] n = len(text) for word in WORDS: if text == word[:n]: matches.append(word) if len(matches) >= state: return matches[state] else: return None
Opět si ukažme, jak může vypadat úplný zdrojový kód využívající vylepšený completer:
import readline WORDS = ("quit", "exit", "eval", "help") def completer(text, state): # print(text, state) matches = [] n = len(text) for word in WORDS: if text == word[:n]: matches.append(word) if len(matches) >= state: return matches[state] else: return None def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) readline.set_completer(completer) readline.parse_and_bind("tab: complete") while True: cmd = input("Command: ") if cmd in {"quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
7. Konfigurace knihovny GNU Readline – soubor .inputrc
Chování knihovny GNU Readline je možné ovlivnit parametry, které jsou zapisovány do souboru nazvaného .inputrc, jenž je umístěn v domácím adresáři uživatele a popř. ze souboru /etc/inputrc (tyto volby budou mít globální platnost). Tento soubor ve skutečnosti může obsahovat jak již zmíněné volby ovlivňující chování GNU Readline, tak i například tzv. makra, což jsou ovšem většinou pouhé expanze kódů kláves posílaných terminálem (typicky escape sekvence) na příkazy typu „přesun kurzoru na začátek zapisovaného textu“. Následuje příklad obsahu ~/.inputrc v případě, že uživatel požaduje, aby se ve všech aplikacích používajících GNU Readline používal režim emulující textové editory Vi/Vim:
set editing-mode vi
Více příkladů, včetně maker, nalezneme v souboru /usr/share/readline popř. v již zmíněném globálním konfiguračním souboru /etc/inputrc. Podívejme se jen na několik příkladů s mapováním escape sekvencí některých kláves na příkazy, které GNU Readline rozpoznává:
# Be 8 bit clean. set input-meta on set output-meta on # allow the use of the Home/End keys "\e[1~": beginning-of-line "\e[4~": end-of-line # allow the use of the Delete/Insert keys "\e[3~": delete-char "\e[2~": quoted-insert # mappings for "page up" and "page down" to step to the beginning/end # of the history # "\e[5~": beginning-of-history # "\e[6~": end-of-history # alternate mappings for "page up" and "page down" to search the history # "\e[5~": history-search-backward # "\e[6~": history-search-forward # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving "\e[1;5C": forward-word "\e[1;5D": backward-word "\e[5C": forward-word "\e[5D": backward-word "\e\e[C": forward-word "\e\e[D": backward-word
$ man readline
8. Knihovna prompt_toolkit
Druhou knihovnou, s jejímiž základními vlastnostmi se v dnešním článku alespoň ve stručnosti seznámíme, je knihovna, která se jmenuje prompt_toolkit. Název této knihovny sice naznačuje, že slouží pro implementaci vstupního (či příkazového) řádku do aplikací, to však není vše. Tato knihovna například umožňuje využít víceřádkový vstupní text, dovoluje použití myši (kromě implicitní funkce myši v terminálu pro operace copy & paste) a dokonce obsahuje sadu prvků uživatelského rozhraní (takzvaných widgetů), mezi něž patří například toolbary, menu, checkboxy, tlačítka, či dialogy. Díky tomu lze tuto knihovnu použít i pro tvorbu aplikací s plnohodnotným textovým uživatelským rozhraním (ne nepodobným starodávnému TurboVision). Dnes si sice ukážeme jen nepatrnou část funkcionality této knihovny, ovšem jejím dalším schopnostem bude později věnován samostatný článek.
9. Instalace knihovny prompt_toolkit
Instalaci knihovny prompt_toolkit provedeme pomocí nástroje pip nebo pip3 (podle zvolené verze Pythonu). Díky použití parametru –user je zajištěno, že se instalace provede do adresáře ~/.local/ (pro aktivního uživatele) a tudíž nemusí mít uživatel rootovská práva:
$ pip3 install --user prompt_toolkit Downloading/unpacking prompt-toolkit Downloading prompt_toolkit-2.0.3-py3-none-any.whl (322kB): 322kB downloaded Downloading/unpacking wcwidth (from prompt-toolkit) Downloading wcwidth-0.1.7-py2.py3-none-any.whl Requirement already satisfied (use --upgrade to upgrade): six>=1.9.0 in ./.local/lib/python3.4/site-packages (from prompt-toolkit) Installing collected packages: prompt-toolkit, wcwidth Successfully installed prompt-toolkit wcwidth Cleaning up...
10. Využití funkce prompt_toolkit.prompt
Ukažme si nyní základní způsob použití funkce prompt_toolkit.prompt, která efektivně nahrazuje výše popsanou standardní funkci input. Pokud nebudeme vyžadovat žádné další pokročilejší schopnosti (kromě plnohodnotné editace vstupního řádku!), může se funkce prompt použít následujícím způsobem:
from prompt_toolkit import prompt cmd = prompt("Command: ") print("Entered: '{cmd}'".format(cmd=cmd))
Tuto funkci můžeme velmi snadno přidat do našeho demonstračního příkladu akceptujícího trojici příkazů a nahradit tak volání input a současně i přímé použití knihovny GNU Readline:
from prompt_toolkit import prompt def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) while True: cmd = prompt("Command: ") if cmd in {"q", "quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
11. Režim zápisu hesla
V některých případech je nutné zajistit, aby se při zadávání údajů na vstupní řádku neprováděl opis (takzvané echo) zapisovaných znaků, ale aby se namísto skutečně zapisovaných znaků pouze ukazoval nějaký zástupný znak, typicky hvězdička. Tento režim se používá například při zadávání hesel a můžeme ho velmi snadno zapnout využitím nepovinného parametru is_password, který musí mít nastavenou hodnotu True. Použití tohoto režimu je snadné, což ostatně dokládá zdrojový kód další varianty našeho demonstračního příkladu:
from prompt_toolkit import prompt def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) while True: cmd = prompt("Command: ", is_password=True) if cmd in {"q", "quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
12. Objekt typu PromptSession
Funkce prompt_toolkit.prompt, kterou jsme si ukázali v předchozích dvou kapitolách, pracuje skutečně jako „pouhá“ funkce bez vnitřního stavu, což v důsledku znamená, že nedokáže zajistit práci s historií již zadaných příkazů (vstupních dat). Pokud podporu pro historii skutečně budeme potřebovat – a mnohdy se jedná o velmi užitečnou vlastnost – musíme namísto funkce prompt_toolkit.prompt využít třídu PromptSession, resp. přesněji řečeno instanci této třídy. Díky tomu, že se namísto funkce prompt_toolkit.prompt bude volat metoda PromptSession.prompt, bude možné (a to bez našeho dalšího zásahu) pracovat s historií vstupního řádku, tj. například listovat historií s využitím kurzorových šipek. Podívejme se na upravený příklad, v němž se objekt typu PromptSession konstruuje a používá:
from prompt_toolkit import PromptSession def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) s = PromptSession() while True: cmd = s.prompt("Command: ") if cmd in {"q", "quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
13. Automatické doplňování příkazů s využitím třídy WordCompleter
Jednou z předností knihovny prompt_toolkit je existence předpřipravených tříd, které lze použít pro automatické doplňování slov nebo výrazů na vstupním řádku. Tříd existuje několik z toho důvodu, že kromě ve své podstatě primitivního doplňování slov na základě připraveného slovníku je možné naprogramovat například doplňování názvů metod v určeném kontextu apod. Dnes si představíme nejjednodušší z těchto tříd – WordCompleter. Tato třída slouží k pouhému automatickému doplňování slov na základě slovníku, tj. ke stejnému účelu, jaký jsme již použili v knihovně GNU Readline. Konstrukce instance třídy WordCompleter je snadná – konstruktoru předáme seznam slov a popř. nepovinný parametr informující o tom, zda se má nebo naopak nemá ignorovat velikost písmen:
c = WordCompleter(["quit", "exit", "help", "eval"], ignore_case=True)
Následně při konstrukci instance třídy PromptSession použijeme nepovinný parametr completer, kterému předáme referenci na dříve vytvořený objekt:
s = PromptSession(completer=c)
To jsou ve skutečnosti jediné dvě změny, které je nutné provést, aby automatické doplňování příkazů fungovalo, a to včetně zobrazení menu s nabídkou možností ve chvíli, kdy není doplnění jednoznačné. Ukažme si tedy úplný zdrojový kód upraveného demonstračního příkladu:
from prompt_toolkit import PromptSession from prompt_toolkit.completion import WordCompleter def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) c = WordCompleter(["quit", "exit", "help", "eval"], ignore_case=True) s = PromptSession(completer=c) while True: cmd = s.prompt("Command: ") if cmd in {"quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
14. Režim emulující příkazy editorů Vi a Vim
Další pro mnohé uživatele (zejména programátory či administrátory) užitečnou vlastností knihovny prompt_toolkit je možnost emulace příkazů textových editorů Vi a Vim. Tyto editory jsou modální, přičemž při jejich emulaci se knihovna implicitně nachází v režimu vstupu (insert mode), takže pro přechod do normálního režimu (normal mode) je nutné stisknout klávesu Esc. Jedna z variant přepnutí knihovny prompt_toolkit do režimu emulace Vi/Vim vypadá takto:
s = PromptSession(completer=c, vi_mode=True)
Celý demonstrační příklad:
from prompt_toolkit import PromptSession from prompt_toolkit.completion import WordCompleter def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) c = WordCompleter(["quit", "exit", "help", "eval"], ignore_case=True) s = PromptSession(completer=c, vi_mode=True) while True: cmd = s.prompt("Command: ") if cmd in {"quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
Druhá možnost využívá nepovinného parametru editing_mode předaného konstruktoru třídy PromptSession. Hodnota tohoto parametru se importuje z modulu enums:
s = PromptSession(completer=c, editing_mode=enums.EditingMode.VI)
Opět si ukažme celý demonstrační příklad:
from prompt_toolkit import PromptSession from prompt_toolkit import enums from prompt_toolkit.completion import WordCompleter def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) c = WordCompleter(["quit", "exit", "help", "eval"], ignore_case=True) s = PromptSession(completer=c, editing_mode=enums.EditingMode.VI) while True: cmd = s.prompt("Command: ") if cmd in {"quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
15. Režim emulující příkazy editoru Emacs
Do režimu emulujícího některé základní příkazy editoru Emacs je možné se přepnout použitím pojmenovaného parametru editing_mode, který musí obsahovat konstantu EditingMode.EMACS naimportovanou z modulu enums. Následující příklad je až na odlišný režim editace totožný s příkladem předchozím, takže jen krátce:
from prompt_toolkit import PromptSession from prompt_toolkit import enums from prompt_toolkit.completion import WordCompleter def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) c = WordCompleter(["quit", "exit", "help", "eval"], ignore_case=True) s = PromptSession(completer=c, editing_mode=enums.EditingMode.EMACS) while True: cmd = s.prompt("Command: ") if cmd in {"quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
16. Použití objektu PromptSession v režimu zadávání hesla
Jen pro úplnost si na závěr ukažme, že parametry funkce prompt a metody PromptSession.prompt jsou skutečně do značné míry shodné. V dnešním posledním příkladu vytvoříme objekt typu PromptSession a při zadávání vstupních dat zvolíme režim zápisu hesla, což se podobá situaci z jedenácté kapitoly:
from prompt_toolkit import PromptSession def show_help(): print("""Help -------- quit - quit this application exit - exit from this application eval - evaluate """) s = PromptSession() while True: cmd = s.prompt("Command: ", is_password=True) if cmd in {"q", "quit", "Quit", "exit", "Exit"}: break elif cmd in {"help", "Help", "?"}: show_help() elif cmd == "eval": print("42")
17. Standardní klávesové zkratky použité knihovnou GNU Readline v režimu Emacs
V této kapitole jsou vypsány vybrané klávesové zkratky, které jsou ve výchozím nastavení použity knihovnou GNU Readline při přepnutí do režimu Emacs.
Příkazy pro přesuny kurzoru
Základní příkazy pro přesun kurzoru používají kombinaci Ctrl+znak, Alt+znak popř. alternativně Esc, znak v případě, že zkratky Alt+znak kolidují s emulátorem terminálu (například vyvolávají příkazy z menu). Pokud je terminál správně nakonfigurován, měly by fungovat i kurzorové šipky a navíc i klávesy Home a End (se zřejmou funkcí):
Klávesa | Význam |
---|---|
Ctrl+B | přesun na předchozí znak |
Ctrl+F | přesun na další znak |
Alt+B | přesun na předchozí slovo |
Alt+F | přesun na další slovo |
Esc, B | shodné s Alt+B |
Esc, F | shodné s Alt+F |
Ctrl+A | přesun na začátek řádku |
Ctrl+E | přesun na konec řádku |
Mazání textu, práce s kill ringem
Pro přesun části textu v rámci editovaného řádku se používá takzvaný kill ring, do něhož se smazaný text uloží. Pro vložení takto smazaného textu do jiné oblasti se používá operace nazvaná yank (odpovídá paste). Některé dále uvedené příkazy dokážou s kill ringem pracovat:
Klávesa | Význam |
---|---|
Ctrl+K | smaže text od kurzoru do konce řádku a uloží ho do kill ringu |
Ctrl+U | smaže text od začátku řádku do pozice kurzoru a uloží ho do kill ringu |
Ctrl+W | smaže předchozí slovo a uloží ho do kill ringu |
Alt+D | smaže následující slovo a uloží ho do kill ringu |
Ctrl+Y | vloží text z kill ringu na místo, na němž se nachází kurzor (yank) |
Alt+Y | po operaci Ctrl+Y dokáže rotovat historií kill ringu a obnovit tak (před)předchozí smazaný text |
Ctrl+D | smaže jeden znak (pokud je ovšem na řádku nějaký obsah, jinak typicky ukončí aplikaci) |
Práce s historií dříve zadaných příkazů
Klávesa | Význam |
---|---|
Ctrl+P | průchod historií – předchozí text |
Ctrl+N | průchod historií – následující text |
Ctrl+R | zpětné (interaktivní) vyhledávání v historii |
Ctrl+G | ukončení režimu vyhledávání |
Některé další dostupné příkazy
Klávesa | Význam |
---|---|
Tab | implicitní klávesa pro zavolání completeru |
Ctrl+T | prohození dvou znaků (před kurzorem a na pozici kurzoru) |
Alt+U | text od pozice kurzoru do konce slova se změní NA VERZÁLKY |
Alt+L | text od pozice kurzoru do konce slova se změní na mínusky |
Alt+C | text od pozice kurzoru do konce slova se změní Tak, Že Slova Začínají Velkým Písmenem |
18. Standardní klávesové zkratky použité knihovnou GNU Readline v režimu Vi
V režimu emulace editorů Vi/Vim je možné mj. použít i tyto klávesové zkratky:
Příkazy pro přesuny kurzoru
Tyto příkazy jsou platné pro normální režim a lze je zkombinovat s operátory (delete, change, yank atd.):
Klávesa | Význam |
---|---|
h | přechod na předchozí znak |
l | přechod na další znak |
b | skok (zpět) na první znak slova |
e | skok na poslední znak slova |
w | skok na první znak následujícího slova |
0 | skok na začátek řádku |
$ | skok na konec řádku |
% | doskok na párovou závorku |
f | skok na specifikovaný znak (find) |
Editace textu
Klávesa | Význam |
---|---|
x | smazání jednoho znaku (odpovídá klávese Delete) |
d | operace smazání textu (musí být následována příkazem pro pohyb kurzoru) |
D | smazání textu od pozice kurzoru do konce řádku |
y | přenos textu do registru _ (musí být následována příkazem pro pohyb kurzoru) |
c | změna textu (musí být následována příkazem pro pohyb kurzoru) |
r | změna jediného znaku |
s | změna jediného znaku a přechod do vkládacího režimu |
p | operace put/paste, vloží smazaný text od pozice kurzoru |
P | operace put/paste, vloží smazaný text před pozicí kurzoru |
Příkazy ve vkládacím režimu
Některé příkazy ve vkládacím režimu odpovídají režimu Emacsu:
Klávesa | Význam |
---|---|
Ctrl+T | prohození dvou znaků (před kurzorem a na pozici kurzoru) |
Ctrl+H | smazání znaku nalevo od kurzoru (odpovídá Backspace) |
Ctrl+R | zpětné (interaktivní) vyhledávání v historii |
Ctrl+W | smaže předchozí slovo a uloží ho do kill ringu |
Ctrl+Y | vloží text z registru _ na místo, na němž se nachází kurzor (yank) |
Další příkazy
Klávesa | Význam |
---|---|
Esc | přepnutí do normálního režimu |
1–9 | prefix pro opakování další operace |
u | vrácení poslední operace (lze opakovat) |
a | append – přechod do režimu vkládání |
i | insert – přechod do režimu vkládání |
~ | změna jednoho znaku: verzálky/kapitálky a zpět |
k | průchod historií – předchozí text |
j | průchod historií – následující text |
18. Repositář s demonstračními příklady
Všechny dnes popisované demonstrační příklady byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:
20. Odkazy na Internetu
- 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - vi(1) – Linux man page
https://linux.die.net/man/1/vi - emacs(1) – Linux man page
https://linux.die.net/man/1/emacs