5  Введение в командную строку

5.1 Командная оболочка

Когда вы открываете линуксовскую командную строку, чаще всего вы сталкиваетесь с интерпретатором bash –– это командная оболочка, которая позволяет отдавать команды вашей операционной системе и компьютеру. В данных материалах мы используем обобщенное shell, включающее в себя разные командные оболочки. Так как обычно команды в командной строке исполняются сразу, знак доллара традиционно используют для обозначения строчки с командой:

```{shell}
$ echo "hi all"

hi all
```

Разумеется, когда дело доходит до больших и сложных программ, то их записывают в скрипты, которым обычно дают расширение .sh и начинают с шебанга. Вот пример простой программы:

#!/bin/bash

echo "Please enter your name: "
read name
echo "Nice to meet you $name"

Если записать эту программу в файл my_script.sh, то ее потом можно запустить следующей командой:

```{shell}
$ sh my_script.sh
```

5.2 Программы для команадной строки

В данном разделе мы опишем несколько базовых программ для командной строки, которые могут быть важны для работы. Для командной строки написано очень много разных программ. Важно понимать, что их все (естественно, после установки, если это необходимо) можно запустить, набрав в консоли название программы. Начнем наше знакомство с программы ls, которая перечисляет (list) содержание папок:

```{shell}
$ ls

01_00_stemmers.png                  01_09_first_transducer.png
01_01_light_switch.jpg              01_10_morphology.png
01_02_light_switch_automaton.png    01_11_transducer_composition.png
01_03_turnstile.jpg                 01_12_morphology2.png
01_04_turnstile_automaton.png       03_01_Russita-feeding.png
01_05_elephant.png                  03_02_Russita-bleeding.png
01_06_elephant_short.png            03_03_Russita-counterfeeding.png
01_07_multiple_words.png            03_04_Russita-counterbleeding.png
01_08_multiple_words_optimized.png
```

У большинства программ есть некоторые однобуквенные аргументы (почему-то их принято называть флагами), которые перечисляют после минуса. Например флаг -l позволяет увидеть файлы в виде таблицы с некоторой дополнительной информацией про каждый файл.

```{shell}
$ ls -l

total 2636
-rw-rw-r-- 1 agricolamz agricolamz 122183 Jan  6 18:52 01_00_stemmers.png
-rw-rw-r-- 1 agricolamz agricolamz 121071 Feb 18  2022 01_01_light_switch.jpg
-rw-rw-r-- 1 agricolamz agricolamz 156956 Jan  6 07:45 01_02_light_switch_automaton.png
-rw-rw-r-- 1 agricolamz agricolamz  18075 Feb 18  2022 01_03_turnstile.jpg
-rw-rw-r-- 1 agricolamz agricolamz 167801 Jan  6 07:47 01_04_turnstile_automaton.png
-rw-rw-r-- 1 agricolamz agricolamz 200090 Jan  6 07:50 01_05_elephant.png
-rw-rw-r-- 1 agricolamz agricolamz 106598 Jan  6 07:51 01_06_elephant_short.png
-rw-rw-r-- 1 agricolamz agricolamz 226555 Jan  6 07:52 01_07_multiple_words.png
-rw-rw-r-- 1 agricolamz agricolamz 139834 Jan  6 07:58 01_08_multiple_words_optimized.png
-rw-rw-r-- 1 agricolamz agricolamz 132286 Jan  6 16:02 01_09_first_transducer.png
-rw-rw-r-- 1 agricolamz agricolamz 117095 Jan  6 17:15 01_10_morphology.png
-rw-rw-r-- 1 agricolamz agricolamz 253314 Jan  6 18:32 01_11_transducer_composition.png
-rw-rw-r-- 1 agricolamz agricolamz 261112 Jan 12 18:20 01_12_morphology2.png
-rwxr-xr-x 1 agricolamz agricolamz 159091 Jan 13  2017 03_01_Russita-feeding.png
-rwxr-xr-x 1 agricolamz agricolamz 150034 Jan 13  2017 03_02_Russita-bleeding.png
-rwxr-xr-x 1 agricolamz agricolamz 140454 Jan 13  2017 03_03_Russita-counterfeeding.png
-rwxr-xr-x 1 agricolamz agricolamz 193216 Jan 13  2017 03_04_Russita-counterbleedin
```

Давайте использовать несколько флагов. Добавим флаг -r для обратной сортировки:

```{shell}
$ ls -l -r

total 2636
-rwxr-xr-x 1 agricolamz agricolamz 193216 Jan 13  2017 03_04_Russita-counterbleeding.png
-rwxr-xr-x 1 agricolamz agricolamz 140454 Jan 13  2017 03_03_Russita-counterfeeding.png
-rwxr-xr-x 1 agricolamz agricolamz 150034 Jan 13  2017 03_02_Russita-bleeding.png
-rwxr-xr-x 1 agricolamz agricolamz 159091 Jan 13  2017 03_01_Russita-feeding.png
-rw-rw-r-- 1 agricolamz agricolamz 261112 Jan 12 18:20 01_12_morphology2.png
-rw-rw-r-- 1 agricolamz agricolamz 253314 Jan  6 18:32 01_11_transducer_composition.png
-rw-rw-r-- 1 agricolamz agricolamz 117095 Jan  6 17:15 01_10_morphology.png
-rw-rw-r-- 1 agricolamz agricolamz 132286 Jan  6 16:02 01_09_first_transducer.png
-rw-rw-r-- 1 agricolamz agricolamz 139834 Jan  6 07:58 01_08_multiple_words_optimized.png
-rw-rw-r-- 1 agricolamz agricolamz 226555 Jan  6 07:52 01_07_multiple_words.png
-rw-rw-r-- 1 agricolamz agricolamz 106598 Jan  6 07:51 01_06_elephant_short.png
-rw-rw-r-- 1 agricolamz agricolamz 200090 Jan  6 07:50 01_05_elephant.png
-rw-rw-r-- 1 agricolamz agricolamz 167801 Jan  6 07:47 01_04_turnstile_automaton.png
-rw-rw-r-- 1 agricolamz agricolamz  18075 Feb 18  2022 01_03_turnstile.jpg
-rw-rw-r-- 1 agricolamz agricolamz 156956 Jan  6 07:45 01_02_light_switch_automaton.png
-rw-rw-r-- 1 agricolamz agricolamz 121071 Feb 18  2022 01_01_light_switch.jpg
-rw-rw-r-- 1 agricolamz agricolamz 122183 Jan  6 18:52 01_00_stemmers.png
```

Все однобуквенные флаги можно соединять вместе:

```{shell}
$ ls -lr

total 2636
-rwxr-xr-x 1 agricolamz agricolamz 193216 Jan 13  2017 03_04_Russita-counterbleeding.png
-rwxr-xr-x 1 agricolamz agricolamz 140454 Jan 13  2017 03_03_Russita-counterfeeding.png
-rwxr-xr-x 1 agricolamz agricolamz 150034 Jan 13  2017 03_02_Russita-bleeding.png
-rwxr-xr-x 1 agricolamz agricolamz 159091 Jan 13  2017 03_01_Russita-feeding.png
-rw-rw-r-- 1 agricolamz agricolamz 261112 Jan 12 18:20 01_12_morphology2.png
-rw-rw-r-- 1 agricolamz agricolamz 253314 Jan  6 18:32 01_11_transducer_composition.png
-rw-rw-r-- 1 agricolamz agricolamz 117095 Jan  6 17:15 01_10_morphology.png
-rw-rw-r-- 1 agricolamz agricolamz 132286 Jan  6 16:02 01_09_first_transducer.png
-rw-rw-r-- 1 agricolamz agricolamz 139834 Jan  6 07:58 01_08_multiple_words_optimized.png
-rw-rw-r-- 1 agricolamz agricolamz 226555 Jan  6 07:52 01_07_multiple_words.png
-rw-rw-r-- 1 agricolamz agricolamz 106598 Jan  6 07:51 01_06_elephant_short.png
-rw-rw-r-- 1 agricolamz agricolamz 200090 Jan  6 07:50 01_05_elephant.png
-rw-rw-r-- 1 agricolamz agricolamz 167801 Jan  6 07:47 01_04_turnstile_automaton.png
-rw-rw-r-- 1 agricolamz agricolamz  18075 Feb 18  2022 01_03_turnstile.jpg
-rw-rw-r-- 1 agricolamz agricolamz 156956 Jan  6 07:45 01_02_light_switch_automaton.png
-rw-rw-r-- 1 agricolamz agricolamz 121071 Feb 18  2022 01_01_light_switch.jpg
-rw-rw-r-- 1 agricolamz agricolamz 122183 Jan  6 18:52 01_00_stemmers.png
```

Кроме того, некоторые однобуквенные флаги имеют неоднобуквенный аналог. Все неоднобуквенные флаги начинаются с двух минусов. Например, флаг -r имеет неоднобуквенный аналог --reverse:

```{shell}
ls --reverse

03_04_Russita-counterbleeding.png   01_07_multiple_words.png
03_03_Russita-counterfeeding.png    01_06_elephant_short.png
03_02_Russita-bleeding.png          01_05_elephant.png
03_01_Russita-feeding.png           01_04_turnstile_automaton.png
01_12_morphology2.png               01_03_turnstile.jpg
01_11_transducer_composition.png    01_02_light_switch_automaton.png
01_10_morphology.png                01_01_light_switch.jpg
01_09_first_transducer.png          01_00_stemmers.png
01_08_multiple_words_optimized.png
```

Достаточно ожидаемо неоднобуквенные флаги нельзя комбинировать так, как комбинируются однобуквенные:

```{shell}
$ ls --reversel

ls: unrecognized option '--reversel'
Try 'ls --help' for more information.
```

Отчёт об ошибке сообщает нам самое важное: где почитать документацию. В большинстве программ это можно сделать, вызвав аргумент --help. Иногда более вменяемый мануал дает команда man.

```{shell}
$ ls --help

Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.

Mandatory arguments to long options are mandatory for short options too.
  -a, --all                  do not ignore entries starting with .
  -A, --almost-all           do not list implied . and ..
      --author               with -l, print the author of each file
  -b, --escape               print C-style escapes for nongraphic characters
      --block-size=SIZE      with -l, scale sizes by SIZE when printing them;
                             e.g., '--block-size=M'; see SIZE format below

  -B, --ignore-backups       do not list implied entries ending with ~
  -c                         with -lt: sort by, and show, ctime (time of last
                             change of file status information);
                             with -l: show ctime and sort by name;
                             otherwise: sort by ctime, newest first

  -C                         list entries by columns
      --color[=WHEN]         color the output WHEN; more info below
  -d, --directory            list directories themselves, not their contents
  -D, --dired                generate output designed for Emacs' dired mode
  -f                         list all entries in directory order
  -F, --classify[=WHEN]      append indicator (one of */=>@|) to entries WHEN
      --file-type            likewise, except do not append '*'
      --format=WORD          across -x, commas -m, horizontal -x, long -l,
                             single-column -1, verbose -l, vertical -C

      --full-time            like -l --time-style=full-iso
  -g                         like -l, but do not list owner
      --group-directories-first
                             group directories before files;
                             can be augmented with a --sort option, but any
                             use of --sort=none (-U) disables grouping

  -G, --no-group             in a long listing, don't print group names
  -h, --human-readable       with -l and -s, print sizes like 1K 234M 2G etc.
      --si                   likewise, but use powers of 1000 not 1024
  -H, --dereference-command-line
                             follow symbolic links listed on the command line
      --dereference-command-line-symlink-to-dir
                             follow each command line symbolic link
                             that points to a directory

      --hide=PATTERN         do not list implied entries matching shell PATTERN
                             (overridden by -a or -A)

      --hyperlink[=WHEN]     hyperlink file names WHEN
      --indicator-style=WORD
                             append indicator with style WORD to entry names:
                             none (default), slash (-p),
                             file-type (--file-type), classify (-F)

  -i, --inode                print the index number of each file
  -I, --ignore=PATTERN       do not list implied entries matching shell PATTERN
  -k, --kibibytes            default to 1024-byte blocks for file system usage;
                             used only with -s and per directory totals

  -l                         use a long listing format
  -L, --dereference          when showing file information for a symbolic
                             link, show information for the file the link
                             references rather than for the link itself

  -m                         fill width with a comma separated list of entries
  -n, --numeric-uid-gid      like -l, but list numeric user and group IDs
  -N, --literal              print entry names without quoting
  -o                         like -l, but do not list group information
  -p, --indicator-style=slash
                             append / indicator to directories
  -q, --hide-control-chars   print ? instead of nongraphic characters
      --show-control-chars   show nongraphic characters as-is (the default,
                             unless program is 'ls' and output is a terminal)

  -Q, --quote-name           enclose entry names in double quotes
      --quoting-style=WORD   use quoting style WORD for entry names:
                             literal, locale, shell, shell-always,
                             shell-escape, shell-escape-always, c, escape
                             (overrides QUOTING_STYLE environment variable)

  -r, --reverse              reverse order while sorting
  -R, --recursive            list subdirectories recursively
  -s, --size                 print the allocated size of each file, in blocks
  -S                         sort by file size, largest first
      --sort=WORD            sort by WORD instead of name: none (-U), size (-S),
                             time (-t), version (-v), extension (-X), width

      --time=WORD            select which timestamp used to display or sort;
                               access time (-u): atime, access, use;
                               metadata change time (-c): ctime, status;
                               modified time (default): mtime, modification;
                               birth time: birth, creation;
                             with -l, WORD determines which time to show;
                             with --sort=time, sort by WORD (newest first)

      --time-style=TIME_STYLE
                             time/date format with -l; see TIME_STYLE below
  -t                         sort by time, newest first; see --time
  -T, --tabsize=COLS         assume tab stops at each COLS instead of 8
  -u                         with -lt: sort by, and show, access time;
                             with -l: show access time and sort by name;
                             otherwise: sort by access time, newest first

  -U                         do not sort; list entries in directory order
  -v                         natural sort of (version) numbers within text
  -w, --width=COLS           set output width to COLS.  0 means no limit
  -x                         list entries by lines instead of by columns
  -X                         sort alphabetically by entry extension
  -Z, --context              print any security context of each file
      --zero                 end each output line with NUL, not newline
  -1                         list one file per line
      --help        display this help and exit
      --version     output version information and exit

The SIZE argument is an integer and optional unit (example: 10K is 10*1024).
Units are K,M,G,T,P,E,Z,Y,R,Q (powers of 1024) or KB,MB,... (powers of 1000).
Binary prefixes can be used, too: KiB=K, MiB=M, and so on.

The TIME_STYLE argument can be full-iso, long-iso, iso, locale, or +FORMAT.
FORMAT is interpreted like in date(1).  If FORMAT is FORMAT1<newline>FORMAT2,
then FORMAT1 applies to non-recent files and FORMAT2 to recent files.
TIME_STYLE prefixed with 'posix-' takes effect only outside the POSIX locale.
Also the TIME_STYLE environment variable sets the default style to use.

The WHEN argument defaults to 'always' and can also be 'auto' or 'never'.

Using color to distinguish file types is disabled both by default and
with --color=never.  With --color=auto, ls emits color codes only when
standard output is connected to a terminal.  The LS_COLORS environment
variable can change the settings.  Use the dircolors(1) command to set it.

Exit status:
 0  if OK,
 1  if minor problems (e.g., cannot access subdirectory),
 2  if serious trouble (e.g., cannot access command-line argument).

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Full documentation <https://www.gnu.org/software/coreutils/ls>
or available locally via: info '(coreutils) ls invocation'  
```

Некоторые аргументы функций принимают значения от пользователей, обычно их разделяют пробелом. Например, аргумент --sort принимает одно из следующих значений: none, size, time, version, extension или width:

```{shell}
$ ls --sort size

01_12_morphology2.png              03_03_Russita-counterfeeding.png
01_11_transducer_composition.png   01_08_multiple_words_optimized.png
01_07_multiple_words.png           01_09_first_transducer.png
01_05_elephant.png                 01_00_stemmers.png
03_04_Russita-counterbleeding.png  01_01_light_switch.jpg
01_04_turnstile_automaton.png      01_10_morphology.png
03_01_Russita-feeding.png          01_06_elephant_short.png
01_02_light_switch_automaton.png   01_03_turnstile.jpg
03_02_Russita-bleeding.png            
```

Некоторые аргументы позволяют пользователям ввести что-то не из заранее заданного списка. Например, это важно при указании путей, имен файлов, интернет-ссылок и т. п.

Следующая программа, с которой мы познакомимся — pwd (print working directory). Это программа позволяет увидеть текущую папку.

```{shell}
$ pwd

/home/agricolamz/work/materials/2025_morphological_transducers/images
```

Заодно мы увидели, что пути в юниксоподобных операционных системах пишут, используя слэш (“forward” slash).

Для того чтобы перейти в какую-нибудь другую папку, используют команду cd (change directory).

```{shell}
$ pwd

/home/agricolamz/work/materials/2025_morphological_transducers/images

$ cd /home/agricolamz/Documents/CV
$ pwd

/home/agricolamz/Documents/CV
```

Стоит знать о возможности дать программе cd относительный путь:

  • . — текущая папка;
  • .. — родительская папка
  • some_name — имя папки в текущей папке
  • - — предыдущая папка, в которой пользователь находился
```{shell}
$ pwd

/home/agricolamz/Documents/CV

$ cd -
$ pwd

/home/agricolamz/work/materials/2025_morphological_transducers/images

$ cd ..
$ pwd

/home/agricolamz/work/materials/2025_morphological_transducers

$ ls -d */
data/  docs/  images/  task_tests/

$ cd data
$ pwd

/home/agricolamz/work/materials/2025_morphological_transducers/data
```

Для создания файла традиционно используют программу touch, хотя это, видимо, не ее прямое предназначение (см. детали в touch --help):

```{shell}
$ ls

04_kabardian_verb.csv

$ touch new_file.txt
$ ls

04_kabardian_verb.csv  new_file.txt
```

Для того чтобы удалить файл, используется программа rm:

```{shell}
$ ls

04_kabardian_verb.csv  new_file.txt

$ rm new_file.txt
$ ls

04_kabardian_verb.csv
```

Для того чтобы создать папку, используют программу mkdir. Для того чтобы удалить — программа rm с аргументом r (recursive).

```{shell}
$ ls

04_kabardian_verb.csv

$ mkdir some_stuff

$ ls

04_kabardian_verb.csv  some_stuff

$ echo "hi there" > some_stuff/new_file.txt

$ ls some_stuff

new_file.txt

$ rm -r some_stuff
$ ls

04_kabardian_verb.csv
```

Рассмотрим теперь несколько команд для работы с текстами. Любой текстовый файл можно прочитать при помощи команды cat:

```{shell}
$ cat 04_kabardian_verb.csv

translation,surface,abs,dir,io,ben/mal/com,dyn,root,pst/pot,dcl
я иду,сокIуэ,сы,,,,о,кIуэ,,
я шел,сыкIуащ,сы,,,,,кIуэ,а,щ
я пойду,сыкIуэнщ,сы,,,,,кIуэ,н,щ
я иду сюда,сыкъокIуэ,сы,къэ,,,о,кIуэ,,
я шел сюда,сыкъэкIуащ,сы,къэ,,,,кIуэ,а,щ
я пойду сюда,сыкъэкIуэнщ,сы,къэ,,,,кIуэ,н,щ
ты идешь,уокIуэ,у,,,,о,кIуэ,,
ты шел,укIуащ,у,,,,,кIуэ,а,щ
ты пойдешь,укIуэнщ,у,,,,,кIуэ,н,щ
ты идешь сюда,укъокIуэ,у,къэ,,,о,кIуэ,,
ты шел сюда,укъэкIуащ,у,къэ,,,,кIуэ,а,щ
ты пойдешь сюда,укъэкIуэнщ,у,къэ,,,,кIуэ,н,щ
мы идем,докIуэ,ды,,,,о,кIуэ,,
мы шли,дыкIуащ,ды,,,,,кIуэ,а,щ
мы пойдем,дыкIуэнщ,ды,,,,,кIуэ,н,щ
мы идем сюда,дыкъокIуэ,ды,къэ,,,о,кIуэ,,
мы шли сюда,дыкъэкIуащ,ды,къэ,,,,кIуэ,а,щ
мы пойдем сюда,дыкъэкIуэнщ,ды,къэ,,,,кIуэ,н,щ
вы идете,фокIуэ,фы,,,,о,кIуэ,,
вы шли,фыкIуащ,фы,,,,,кIуэ,а,щ
вы пойдете,фыкIуэнщ,фы,,,,,кIуэ,н,щ
вы идете сюда,фыкъокIуэ,фы,къэ,,,о,кIуэ,,
вы шли сюда,фыкъэкIуащ,фы,къэ,,,,кIуэ,а,щ
вы пойдете сюда,фыкъэкIуэнщ,фы,къэ,,,,кIуэ,н,щ
я для тебя иду,сыпхуокIуэ,сы,,п,хуэ,о,кIуэ,,
я для тебя ходил,сыпхуэкIуащ,сы,,п,хуэ,,кIуэ,а,щ
я для тебя пойду,сыпхуэкIуэнщ,сы,,п,хуэ,,кIуэ,н,щ
я против твоей воли иду,сыпфIокIуэ,сы,,п,фIэ,о,кIуэ,,
я против твоей воли ходил,сыпфIыкIуащ,сы,,п,фIэ,,кIуэ,а,щ
я против твоей воли пойду,сыпфIыкIуэнщ,сы,,п,фIэ,,кIуэ,н,щ
я с тобой иду,сыбдокIуэ,сы,,п,дэ,о,кIуэ,,
я с тобой ходил,сыбдэкIуащ,сы,,п,дэ,,кIуэ,а,щ
я с тобой пойду,сыбдэкIуэнщ,сы,,п,дэ,,кIуэ,н,щ
я с вами иду,сывдокIуэ,сы,,ф,дэ,о,кIуэ,,
я с вами ходил,сывдэкIуащ,сы,,ф,дэ,,кIуэ,а,щ
я с вами пойду,сывдэкIуэнщ,сы,,ф,дэ,,кIуэ,н,щ
ты со мной идешь,уздокIуэ,у,,с,дэ,о,кIуэ,,
ты со мной ходил,уздэкIуащ,у,,с,дэ,,кIуэ,а,щ
ты со мной пойдешь,уздэкIуэнщ,у,,с,дэ,,кIуэ,н,щ
ты с нами идешь,уддокIуэ,у,,т,дэ,о,кIуэ,,
ты с нами ходил,уддэкIуащ,у,,т,дэ,,кIуэ,а,щ
ты с нами пойдешь,уддэкIуэнщ,у,,т,дэ,,кIуэ,н,щ
я для вас иду,сыфхуокIуэ,сы,,ф,хуэ,о,кIуэ,,
я для вас ходил,сыфхуэкIуащ,сы,,ф,хуэ,,кIуэ,а,щ
я для вас пойду,сыфхуэкIуэнщ,сы,,ф,хуэ,,кIуэ,н,щ
я против вашей воли иду,сыффIокIуэ,сы,,ф,фIэ,о,кIуэ,,
я против вашей воли ходил,сыффIэкIуащ,сы,,ф,фIэ,,кIуэ,а,щ
я против вашей воли пойду,сыффIэкIуэнщ,сы,,ф,фIэ,,кIуэ,н,щ
ты для меня идешь,усхуокIуэ,у,,с,хуэ,о,кIуэ,,
ты против моей воли идешь,усфIокIуэ,у,,с,фIэ,о,кIуэ,,
ты для нас идешь,утхуокIуэ,у,,т,хуэ,о,кIуэ,,
ты против нашей воли идешь,утфIокIуэ,у,,т,фIэ,о,кIуэ,,
```

Не очень приятно выглядит… Но это связано с тем, что это .csv файл, для его чтения и отображения следует использовать другие инструменты, например, команду column:

```{shell}
$ column -s, -t 04_kabardian_verb.csv

translation                 surface       abs  dir  io  ben/mal/com  dyn  root  pst/pot  dcl
я иду                       сокIуэ        сы                         о    кIуэ           
я шел                       сыкIуащ       сы                              кIуэ  а        щ
я пойду                     сыкIуэнщ      сы                              кIуэ  н        щ
я иду сюда                  сыкъокIуэ     сы   къэ                   о    кIуэ           
я шел сюда                  сыкъэкIуащ    сы   къэ                        кIуэ  а        щ
я пойду сюда                сыкъэкIуэнщ   сы   къэ                        кIуэ  н        щ
ты идешь                    уокIуэ        у                          о    кIуэ           
ты шел                      укIуащ        у                               кIуэ  а        щ
ты пойдешь                  укIуэнщ       у                               кIуэ  н        щ
ты идешь сюда               укъокIуэ      у    къэ                   о    кIуэ           
ты шел сюда                 укъэкIуащ     у    къэ                        кIуэ  а        щ
ты пойдешь сюда             укъэкIуэнщ    у    къэ                        кIуэ  н        щ
мы идем                     докIуэ        ды                         о    кIуэ           
мы шли                      дыкIуащ       ды                              кIуэ  а        щ
мы пойдем                   дыкIуэнщ      ды                              кIуэ  н        щ
мы идем сюда                дыкъокIуэ     ды   къэ                   о    кIуэ           
мы шли сюда                 дыкъэкIуащ    ды   къэ                        кIуэ  а        щ
мы пойдем сюда              дыкъэкIуэнщ   ды   къэ                        кIуэ  н        щ
вы идете                    фокIуэ        фы                         о    кIуэ           
вы шли                      фыкIуащ       фы                              кIуэ  а        щ
вы пойдете                  фыкIуэнщ      фы                              кIуэ  н        щ
вы идете сюда               фыкъокIуэ     фы   къэ                   о    кIуэ           
вы шли сюда                 фыкъэкIуащ    фы   къэ                        кIуэ  а        щ
вы пойдете сюда             фыкъэкIуэнщ   фы   къэ                        кIуэ  н        щ
я для тебя иду              сыпхуокIуэ    сы        п   хуэ          о    кIуэ           
я для тебя ходил            сыпхуэкIуащ   сы        п   хуэ               кIуэ  а        щ
я для тебя пойду            сыпхуэкIуэнщ  сы        п   хуэ               кIуэ  н        щ
я против твоей воли иду     сыпфIокIуэ    сы        п   фIэ          о    кIуэ           
я против твоей воли ходил   сыпфIыкIуащ   сы        п   фIэ               кIуэ  а        щ
я против твоей воли пойду   сыпфIыкIуэнщ  сы        п   фIэ               кIуэ  н        щ
я с тобой иду               сыбдокIуэ     сы        п   дэ           о    кIуэ           
я с тобой ходил             сыбдэкIуащ    сы        п   дэ                кIуэ  а        щ
я с тобой пойду             сыбдэкIуэнщ   сы        п   дэ                кIуэ  н        щ
я с вами иду                сывдокIуэ     сы        ф   дэ           о    кIуэ           
я с вами ходил              сывдэкIуащ    сы        ф   дэ                кIуэ  а        щ
я с вами пойду              сывдэкIуэнщ   сы        ф   дэ                кIуэ  н        щ
ты со мной идешь            уздокIуэ      у         с   дэ           о    кIуэ           
ты со мной ходил            уздэкIуащ     у         с   дэ                кIуэ  а        щ
ты со мной пойдешь          уздэкIуэнщ    у         с   дэ                кIуэ  н        щ
ты с нами идешь             уддокIуэ      у         т   дэ           о    кIуэ           
ты с нами ходил             уддэкIуащ     у         т   дэ                кIуэ  а        щ
ты с нами пойдешь           уддэкIуэнщ    у         т   дэ                кIуэ  н        щ
я для вас иду               сыфхуокIуэ    сы        ф   хуэ          о    кIуэ           
я для вас ходил             сыфхуэкIуащ   сы        ф   хуэ               кIуэ  а        щ
я для вас пойду             сыфхуэкIуэнщ  сы        ф   хуэ               кIуэ  н        щ
я против вашей воли иду     сыффIокIуэ    сы        ф   фIэ          о    кIуэ           
я против вашей воли ходил   сыффIэкIуащ   сы        ф   фIэ               кIуэ  а        щ
я против вашей воли пойду   сыффIэкIуэнщ  сы        ф   фIэ               кIуэ  н        щ
ты для меня идешь           усхуокIуэ     у         с   хуэ          о    кIуэ           
ты против моей воли идешь   усфIокIуэ     у         с   фIэ          о    кIуэ           
ты для нас идешь            утхуокIуэ     у         т   хуэ          о    кIуэ           
ты против нашей воли идешь  утфIокIуэ     у         т   фIэ          о    кIуэ                
```

Достаточно популярные базовые программы для работы с текстовыми данными это wc и grep. Программа wc выводит подсчет относительно файла: строчек, слов и байт.

```{shell}
$ wc 04_kabardian_verb.csv

53  181 3796 04_kabardian_verb.csv
```

При помощи аргументов можно выбрать, что выводить:

```{shell}
$ wc -l 04_kabardian_verb.csv

53 04_kabardian_verb.csv

$ wc -lw 04_kabardian_verb.csv

53 181 04_kabardian_verb.csv

$ wc -m 04_kabardian_verb.csv

2314 04_kabardian_verb.csv
```

Файлов может быть несколько:

```{shell}
$ echo "hi there\n and good bye" > new_file.txt
$ wc 04_kabardian_verb.csv new_file.txt

  53  181 3796 04_kabardian_verb.csv
   2    5   24 new_file.txt
  55  186 3820 total

$ rm new_file.txt
```

Команда grep ищет некоторое выражение в текстовых файлах и является достаточно мощным инструментом.

```{shell}
grep иду 04_kabardian_verb.csv

я иду,сокIуэ,сы,,,,о,кIуэ,,
я иду сюда,сыкъокIуэ,сы,къэ,,,о,кIуэ,,
я для тебя иду,сыпхуокIуэ,сы,,п,хуэ,о,кIуэ,,
я против твоей воли иду,сыпфIокIуэ,сы,,п,фIэ,о,кIуэ,,
я с тобой иду,сыбдокIуэ,сы,,п,дэ,о,кIуэ,,
я с вами иду,сывдокIуэ,сы,,ф,дэ,о,кIуэ,,
я для вас иду,сыфхуокIуэ,сы,,ф,хуэ,о,кIуэ,,
я против вашей воли иду,сыффIокIуэ,сы,,ф,фIэ,о,кIуэ,,

grep -E "иду|идет" 04_kabardian_verb.csv

я иду,сокIуэ,сы,,,,о,кIуэ,,
я иду сюда,сыкъокIуэ,сы,къэ,,,о,кIуэ,,
вы идете,фокIуэ,фы,,,,о,кIуэ,,
вы идете сюда,фыкъокIуэ,фы,къэ,,,о,кIуэ,,
я для тебя иду,сыпхуокIуэ,сы,,п,хуэ,о,кIуэ,,
я против твоей воли иду,сыпфIокIуэ,сы,,п,фIэ,о,кIуэ,,
я с тобой иду,сыбдокIуэ,сы,,п,дэ,о,кIуэ,,
я с вами иду,сывдокIуэ,сы,,ф,дэ,о,кIуэ,,
я для вас иду,сыфхуокIуэ,сы,,ф,хуэ,о,кIуэ,,
я против вашей воли иду,сыффIокIуэ,сы,,ф,фIэ,о,кIуэ,,
```

Последнее, что следует обсудить в данном разделе — оператор |. Этот оператор позволяет отправить результат работы одной функции в другую, например:

```{shell}
$ cat 04_kabardian_verb.csv | wc -l

53
```

В данном случае программа wc приняла результат работы cat. Это полезный инструмент, если хочется запустить несколько программ, не делая промежуточных переменных. Вот еще один пример:

```{shell}
$ ls ../images | sort -r

03_04_Russita-counterbleeding.png
03_03_Russita-counterfeeding.png
03_02_Russita-bleeding.png
03_01_Russita-feeding.png
01_12_morphology2.png
01_11_transducer_composition.png
01_10_morphology.png
01_09_first_transducer.png
01_08_multiple_words_optimized.png
01_07_multiple_words.png
01_06_elephant_short.png
01_05_elephant.png
01_04_turnstile_automaton.png
01_03_turnstile.jpg
01_02_light_switch_automaton.png
01_01_light_switch.jpg
01_00_stemmers.png
```

Не вдаваясь в подробности, стоит оговориться, что если мы хотим записать результат работы функции в файл, то для этого используется другой оператор: >. Мы видели выше его использование:

```{shell}
$ echo "hi there" > new_file.txt
```

5.3 Компиляция трансдьюсеров

Теперь попробуем шаг за шагом скомпилировать наш морфологический трансдьюсер, используя программы lexd и hfst. Первым делом нужно установить необходимые программы hfst, lexd:

```{shell}
$ curl -s https://apertium.projectjj.com/apt/install-nightly.sh | sudo bash
$ sudo apt-get install hfst lexd
```

Создадим файлы example.lexd и example.twol:

```{lexd}
PATTERNS
Noun (Suffix[-adj] | (Suffix[adj] Inflection))?

LEXICON Noun
ночь
печь

LEXICON Suffix
<dim>:ка
<adj>:н[adj]

LEXICON Inflection
<m><sg><nom>:ой
```
```{twol}
Alphabet
  а е й к н о п ч ь ь:0;

Rules

"чк чн пишется без ь"
! например, ночьной -> ночной или печька -> печка

ь:0 <=> _ к;
        _ н;
```

Скомпилируем .lexd файл:

```{shell}
$ lexd example.lexd

0   1   н   н   0.000000    
0   2   п   п   0.000000    
1   3   о   о   0.000000    
2   3   е   е   0.000000    
3   4   ч   ч   0.000000    
4   5   ь   ь   0.000000    
5   6   <dim>   к   0.000000    
5   7   <adj>   н   0.000000    
6   8   @0@ а   0.000000    
7   9   <m> о   0.000000    
9   10  <sg>    й   0.000000    
10  8   <nom>   @0@ 0.000000    
5   0.000000
8   0.000000 
```

Мы видим, что программа lexd преобразовала наш файл example.lexd в трансдьюсер в ATT формате (см. Раздел 1.4.1). Так как основной формат все равно завязан на программу hfst, можно перенаправить ATT в команду hfst-txt2fst. Флаг -o отвечает за имя файла, куда записать результат, так что пользователь может задать любое свое имя, отличное от lexd.hfst.

```{shell}
$ lexd example.lexd | hfst-txt2fst -o lexd.hfst
```

В результате был получен бинарный файл .hfst, который используется для дальнейшей работы. Например, можно использовать функцию hfst-lookup для того, чтобы обратиться к трансдьюсеру.

```{shell}
$ echo "ночь" | hfst-lookup lexd.hfst 

hfst-lookup: Warning: It is not possible to perform fast lookups with OpenFST, std arc, tropical semiring format automata.
Using HFST basic transducer format and performing slow lookups
> ночь  ночь    0,000000

$ echo "ночька" | hfst-lookup lexd.hfst

hfst-lookup: Warning: It is not possible to perform fast lookups with OpenFST, std arc, tropical semiring format automata.
Using HFST basic transducer format and performing slow lookups
> ночька    ночька+?    inf
```

Можно использовать программу hfst-fst2strings, чтобы посмотреть на все формы:

```{shell}
$ hfst-fst2strings lexd.hfst

ночь
ночь<dim>:ночька
ночь<adj><m><sg><nom>:ночьной
печь
печь<dim>:печька
печь<adj><m><sg><nom>:печьной
```

Теперь можно скомпилировать .twol часть:

```{shell}
$ hfst-twolc example.twol -o twol.hfst

Reading input from task.twol.
Writing output to twol.hfst.
Reading alphabet.
Reading rules and compiling their contexts and centers.
Compiling rules.
Storing rules.
```

Все это достаточно скучные сообщения, поэтому можно использовать аргумент -q (quiet), чтобы их не было.

```{shell}
$ hfst-twolc -q example.twol -o twol.hfst
```

Может быть вам захотелось бы посмотреть на twol.hfst, но …

```{shell}
$ hfst-fst2strings twol.hfst

hfst-fst2strings: Error: Transducer is cyclic. Use one or more of these options: -n, -N, -r, -l, -L, -c
```

Давайте воспользуемся аргументом -n:

```{shell}
$ hfst-fst2strings -n 10 twol.hfst

ь
ььк:ьк
ьькь:ькь
ьькььк:ькьк
ьькьькь:ькькь
ьькьькььк:ькькьк
ьькьькьькь:ькькькь
ьькьькьькььк:ькькькьк
ьькьькьькьькь:ькькькькь
ьькьькьькьькььк:ькькькькьк

$ hfst-fst2strings -r 10 twol.hfst

@_IDENTITY_SYMBOL_@@#@@#@:@_IDENTITY_SYMBOL_@
а
е
к
н
нк
о
оье
п
ь@_IDENTITY_SYMBOL_@й 
```

Я считаю, что из преведенных фрагментов достаточно сложно что-то понять. Но попробуем пересечь наши трансдьюсеры:

```{shell}
$ hfst-compose-intersect lexd.hfst twol.hfst -o result.hfst
```

Теперь мы можем посмотреть, что же у нас получилось:

```{shell}
$ hfst-fst2strings result.hfst    

ночь<dim>:ночка
ночь<adj><m><sg><nom>:ночной
ночь
печь<dim>:печка
печь<adj><m><sg><nom>:печной
печь
```

Все! Мы сумели скомпелировать трансдьюсер, не используя Makefile, которым мы пользовались все это время.