# Bash

# Bash общая информация

Консоль работает с текстом, поэтому центральная задача - обработка текста.

<table border="1" id="bkmrk-echo-%D0%9F%D1%80%D0%BE%D1%81%D1%82%D0%BE-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4%D0%B8%D1%82-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 18.631%;"></col><col style="width: 81.4882%;"></col></colgroup><tbody><tr><td>echo</td><td>Просто выводит строку</td></tr><tr><td>printf</td><td>Выводит строку с модификаторами

printf "%s %d" "ff" 22

</td></tr></tbody></table>

Поиск приложения: whereis name

~ - домашняя директория, ~sergey домашняя директория пользователя sergey

cd без аргументов в домашнюю, cd - в предыдущую

Маски для ls

<table border="1" id="bkmrk-%3F-%D0%BE%D0%B4%D0%B8%D0%BD-%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB-%2A-%D0%BC%D0%BD%D0%BE%D0%B3" style="border-collapse: collapse; width: 100%; height: 152.781px;"><colgroup><col style="width: 18.631%;"></col><col style="width: 81.4882%;"></col></colgroup><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">?</td><td style="height: 29.7969px;">один символ</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">\*</td><td style="height: 29.7969px;">много символов</td></tr><tr style="height: 63.3906px;"><td style="height: 63.3906px;">\[set\]</td><td style="height: 63.3906px;">Набор символов, в данном случае s,e,t

\[a-c\] a,b,c

\[!0-9\] где нет цифр

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">{}</td><td style="height: 29.7969px;">перечисляются наборы текста, например echo f{io,am}

</td></tr></tbody></table>

Утилиты фильтрации текста

<table border="1" id="bkmrk-cat-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.6548%;"></col><col style="width: 74.4644%;"></col></colgroup><tbody><tr><td>cat</td><td>перенаправление ввода на вывод

-b - нумеровать только непустые строки;  
-E - показывать символ $ в конце каждой строки;  
-n - нумеровать все строки;  
-s - удалять пустые повторяющиеся строки

cat file1 file2 &gt; file3

</td></tr><tr><td>grep</td><td>поиск строк во вводе

Может анализировать один файл или директорию.

-e несколько регулярных выражений, например -e ... -e ...

-n номер строки, где найдено совпадение

-i, --ignore-case - не учитывать регистр символов;  
-v, --invert-match - вывести только те строки, в которых шаблон поиска не найден;  
-w, --word-regexp - искать шаблон как слово, отделенное пробелами или другими знаками препинания;  
-x, --line-regexp - искать шаблон как целую строку, от начала и до символа перевода строки;  
-c - вывести количество найденных строк;  
-L, --files-without-match - выводить только имена файлов, будут выведены все файлы в которых выполняется поиск;  
-l, --files-with-match - аналогично предыдущему, но будут выведены только файлы, в которых есть хотя бы одно вхождение;  
-m, --max-count - остановить поиск после того как будет найдено указанное количество строк;  
-o, --only-matching - отображать только совпавшую часть, вместо отображения всей строки;  
-h, --no-filename - не выводить имя файла;  
-s, --no-messages - не выводить ошибки чтения файлов;  
-A, --after-content - показать вхождение и n строк после него; Те -A2 строка + 2 строки после  
-B, --before-content - показать вхождение и n строк перед ним;  
-C - показать n строк до и после вхождения;  
-a, --text - обрабатывать двоичные файлы как текст;  
\--exclude - пропустить файлы имена которых соответствуют регулярному выражению;  
\--exclude-dir - пропустить все файлы в указанной директории;  
-I - пропускать двоичные файлы;  
\--include - искать только в файлах, имена которых соответствуют регулярному выражению;  
-r - рекурсивный поиск по всем подпапкам;  
-R - рекурсивный поиск включая ссылки;

```bash
grep -r "поисковой_запрос" /путь/к/директории/
```

</td></tr><tr><td>sort</td><td>сортировка строк

-b - не учитывать пробелы  
-d - использовать для сортировки только буквы и цифры  
-i - сортировать только по ASCII символах  
-n - сортировка строк linux по числовому значению  
-r - сортировать в обратном порядке  
-o - вывести результат в файл  
-u - игнорировать повторяющиеся строки  
-m - объединение ранее отсортированных файлов  
-k - указать столбец по которому нужно сортировать строки, если не задано, сортировка выполняется по всей строке.

ls -l | sort -k9  
-f - использовать в качестве разделителя полей ваш символ вместо пробела.

</td></tr><tr><td>uniq f1 &gt; f2</td><td>Удаляет повторяющиеся строки

</td></tr><tr><td>cut</td><td>извлечение символов из ввода

-b 2-6, 8 отобразит символы от 2 по 6 из строки и 8

-b 5- символы с 5 до конца

-с нечто похожее на -b

-d ':' -f 1,3 считать разделителем : и оставить первый и третий столбцы

</td></tr><tr><td>sed</td><td>операции изменения данных перед выводом

Позволяет вырезать строки, поиск/замена по регулярке, вывод. Крутая штука.

</td></tr><tr><td>tr</td><td>транслирование символов на вводе в другие символы

-d набор - удаление символов

 tr -d '0-9'

-s заменяет последовательность повторяющихся символов в SET1 на один такой символ;

 tr -s ' ' заменит много пробелов на один

</td></tr><tr><td>head</td><td>-n кол-во Выводит первые кол-во строк</td></tr><tr><td>awk</td><td>Считывает построчно данные, выполняет действия и выводит в stdout. Включает целый язык для обработки текста.

awk опции 'условие {действие} условие {действие}'

Опции:

-F, --field-separator - разделитель полей, используется для разбиения текста на колонки;  
-f, --file - прочитать данные не из стандартного вывода, а из файла;  
-v, --assign - присвоить значение переменной, например foo=bar;  
-b, --characters-as-bytes - считать все символы однобайтовыми;  
-d, --dump-variables - вывести значения всех переменных awk по умолчанию;  
-D, --debug - режим отладки, позволяет вводить команды интерактивно с клавиатуры;  
-e, --source - выполнить указанный код на языке awk;  
-o, --pretty-print - вывести результат работы программы в файл;

Действия:

print(строка) - вывод чего либо в стандартный поток вывода;  
printf(строка) - форматированный вывод в стандартный поток вывода;  
system(команда) - выполняет команду в системе;  
length(строка) - возвращает длину строки;  
substr(строка, старт, количество) - обрезает строку и возвращает результат;  
tolower(строка) - переводит строку в нижний регистр;  
toupper(строка) - переводить строку в верхний регистр.

Переменные:

$NF - последняя строка

$(NF-1) - предпоследняя

NR - количество строк с начала

awk -F":" '{print $4}' Возьмет строку и по разделителю : выведет 4 элемент

Поддерживает регулярные выражения

echo -e 'one 1\\n two 2' | awk '{sum+=$2} END {print sum}' сумма элементов.

awk 'NR &lt; 10' log.txt

</td></tr><tr><td>sed</td><td>Тоже очень крутая штука, этим двум командам посвящена целая книга.

</td></tr><tr><td>diff file1 file2</td><td>Различие в строках

</td></tr><tr><td>ndiff</td><td>Различие в результатах сканирования nmap

```
${BIN_PATH}ndiff $BASE_RESULTS $NEW_RESULTS > $NDIFF_RESULTS
```

</td></tr><tr><td>jq</td><td>Утилита для анализа JSON.

jq -r '.first' возвращает значение ключа first

jq '.\[0\].plugins.HTTPServer.string\[0\] определяет следующий элемент:

```json
[
  {
    "plugins": {
      "HTTPServer": {
        "string": [
          "Werkzeug/3.0.1 Python/3.12.3"
        ]
      }
    }
  }
]
```

</td></tr><tr><td>tail -F filename</td><td>Отображения изменения в файле

</td></tr><tr><td>wget</td><td>Загрузка файлов

-q тихий режим

-r рекурсивное скачивание

-np скачивание файлов из данной директории или ниже

-R "index.html" исключить загрузку файлов вида index.html

-P foldername директория сохранения

</td></tr><tr><td>curl</td><td>Must have.

-s тихий режим

-X GET|POST|PUT|DELETE → явный выбор метода

-d "param=value" → данные формы (POST)

```
curl -X POST -H "Content-Type: application/json" \
     -d '{"name":"Alice"}' https://api.example.com/users
```

-H "Header: value" → добавить заголовок

```
curl -H "Authorization: Bearer TOKEN" https://api.example.com
```

-b "name=value" → передать cookie  
-c cookies.txt → сохранить cookie в файл  
-b cookies.txt → использовать cookie из файла

-o file → сохранить в файл

 -u user:pass → basic auth

-k → игнорировать ошибки сертификатов (небезопасно!)  
\--cacert file.crt → указать свой сертификат CA

-w "%{http\_code}\\n" → вывести только HTTP-код

</td></tr><tr><td>find / -name "file.txt"</td><td>Поиск файла

</td></tr></tbody></table>

**Редиректы**

Вывод в файл:

<table id="bkmrk-%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80-%D0%97%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9F%D1%80"><thead><tr><th>Оператор</th><th>Значение</th><th>Пример</th></tr></thead><tbody><tr><td>`>`</td><td>Перенаправить вывод (перезаписать файл)</td><td>`echo "hi" > file.txt`</td></tr><tr><td>`>>`</td><td>Перенаправить вывод (добавить в файл)</td><td>`echo "hi" >> file.txt`</td></tr></tbody></table>

Вывод из файла:

<table id="bkmrk-%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80-%D0%97%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9F%D1%80-1"><thead><tr><th>Оператор</th><th>Значение</th><th>Пример</th></tr></thead><tbody><tr><td>`<`</td><td>Чтение из файла</td><td>`wc -l < file.txt`</td></tr><tr><td>`<<`</td><td>**Here document** — передаёт блок текста как stdin</td><td>cat &lt;&lt; EOFстрока1строка2EOF</td></tr><tr><td>`<<<`</td><td>**Here string** — передаёт одну строку (или переменную)</td><td>`grep foo <<< "foo bar baz"`</td></tr></tbody></table>

**Фоновые задачи**

Фоновые задачи не получают данные от клавиатуры

<table id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%A7%D1%82%D0%BE-%D0%B4%D0%B5%D0%BB%D0%B0%D0%B5%D1%82-c" style="width: 99.6429%;"><thead><tr><th style="width: 22.294%;">Команда</th><th style="width: 77.706%;">Что делает</th></tr></thead><tbody><tr><td style="width: 22.294%;">`command &`</td><td style="width: 77.706%;">Запускает процесс в фоне</td></tr><tr><td style="width: 22.294%;">`jobs`</td><td style="width: 77.706%;">Показывает список фоновых задач (с job ID: `%1`, `%2` …)</td></tr><tr><td style="width: 22.294%;">`fg %N`</td><td style="width: 77.706%;">Переводит задачу номер N в передний план</td></tr><tr><td style="width: 22.294%;">`bg %N`</td><td style="width: 77.706%;">Возобновляет приостановленную задачу в фоне</td></tr><tr><td style="width: 22.294%;">`Ctrl+Z`</td><td style="width: 77.706%;">Приостанавливает текущий процесс (отправляет в «stopped»)</td></tr><tr><td style="width: 22.294%;">`kill %N`</td><td style="width: 77.706%;">Завершает задачу по её job ID</td></tr><tr><td style="width: 22.294%;">`kill PID`</td><td style="width: 77.706%;">Завершает задачу по PID</td></tr><tr><td style="width: 22.294%;">`kill -9 PID`</td><td style="width: 77.706%;">Жёсткое завершение (если обычный `kill` не помог)</td></tr><tr><td style="width: 22.294%;">`disown %N`</td><td style="width: 77.706%;">«Отвязывает» задачу от текущего терминала (будет жить после выхода)</td></tr><tr><td style="width: 22.294%;">`ps aux</td><td style="width: 77.706%;">grep name`</td></tr><tr><td style="width: 22.294%;">`pkill -f pattern`</td><td style="width: 77.706%;">Завершение процессов по шаблону</td></tr></tbody></table>

Задачи завершаются при закрытии сессии. Через команду nohup можно сделать задачу, работающую после закрытия сессии.

```
nohup ./myscript.sh &
```

**История ввода команд**

Хранится где-то в сессии, сохраняется в .bash\_history при закрытии сессии. Для просмотра введенных команд в пределах сессии используется утилита fc

<table border="1" id="bkmrk--l-%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BD%D0%B8%D0%B5-15-%D0%BA%D0%BE%D0%BC%D0%B0" style="border-collapse: collapse; width: 100%; height: 368.953px;"><colgroup><col style="width: 31.4882%;"></col><col style="width: 68.631%;"></col></colgroup><tbody><tr style="height: 80.1875px;"><td style="height: 80.1875px;">-l</td><td style="height: 80.1875px;">Последние 15 команд

4 выведет четвертую команду

4 7 команды с 4 по 7

строка - выведет с первого вхождения строки (из 15) и далее

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">-n</td><td style="height: 29.7969px;">без вывода номеров</td></tr><tr style="height: 80.1875px;"><td style="height: 80.1875px;">-e nano</td><td style="height: 80.1875px;">без дальнейших аргументов - редактирование последней команды и затем исполнение

Чтобы не вводить редактор, можно в .bash\_profile добавить переменную

FCEDIT=nano

</td></tr></tbody></table>

!! исполнение предыдущей команды

**Специальные файлы**

.bash\_profile, .bash\_logout, .bashrc Если отсутствуют - используются файл /etc/profile и файлы из /etc/profile.d

**.bash\_profile - сейчас не используется.**

В .profile переменные окружения, .bashrc - скрипты. .bash\_login при входе. Но идея осталась.

Последовательность поиска при авторизации, исполняется первый найденный

- `~/.bash_profile`
- `~/.bash_login`
- `~/.profile`

Исполняет команды при каждом входе в систему.

Итого:

~/.bashrc

Используется в интерактивных (обычных) shell-ах. Сюда кладём то, что нужно только в терминале:

- алиасы (alias ll='ls -lh')
- функции (mkcd() { mkdir -p "$1" &amp;&amp; cd "$1"; })
- настройки PS1 (prompt, цвета)
- shopt и настройки истории (HISTSIZE, HISTCONTROL)
- подключение fzf, автодополнений и прочих «тюнингов»

~/.profile

Используется при login-shell (и не только Bash, а sh/dash/zsh тоже могут его читать). Сюда кладём:

- переменные окружения (PATH, EDITOR, LANG, JAVA\_HOME)
- настройки, которые должны работать и в GUI-программах (например, экспорт GTK\_THEME или XMODIFIERS)
- запуск программ при логине (например, ssh-agent или gnome-keyring-daemon)

~/.bash\_profile или ~/.bash\_login

Сегодня почти не нужны. Если они есть, то Bash не будет читать ~/.profile.

source .profile - перечитать файл

**Alias, options и переменные окружения**

Алиасы для команд. Создание алиасов: alias cdvoy='cd sipp/demo/animation/voyager'

Пробел перед закрывающей ' означает ожидание ввода пользователя.

unalias name - удаление

set +o optionname установить опцию, -o убрать. Фиксированный набор. Не впечатлило.

**Переменные**

Синтаксис: varname='something', использование $varname удаление unset varname

Формат timestamp:

<table border="1" id="bkmrk-%25a%2C-%25a-%D0%90%D0%B1%D0%B1%D1%80%D0%B5%D0%B2%D0%B8%D0%B0%D1%82%D1%83%D1%80%D0%B0-" style="border-collapse: collapse; width: 100%; height: 268.172px;"><colgroup><col style="width: 17.0441%;"></col><col style="width: 82.9559%;"></col></colgroup><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%a, %A</td><td style="height: 29.7969px;">Аббревиатура дня недели, Полное имя дня недели</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%b, %B, %m</td><td style="height: 29.7969px;">Аббревиатура имени месяца, Полное имя месяца, Номер месяца в числовом формате</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%c</td><td style="height: 29.7969px;">Дата и время локали</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%C</td><td style="height: 29.7969px;">Последние 2 цифры года

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%H, %I, %p</td><td style="height: 29.7969px;">Час в 24-часовом формате, Час в 12-часовом формате, эквивалент am/pm</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%d, %e</td><td style="height: 29.7969px;">Цифра дня, цифра дня с пробелом в случае одной цифры</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%D</td><td style="height: 29.7969px;">Дата в американском формате (%m/%d/%y)</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%j</td><td style="height: 29.7969px;">День года 001-366</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%M</td><td style="height: 29.7969px;">Минута в десятичном представлении</td></tr><tr><td>%n, %t</td><td>Новая строка, табуляция</td></tr><tr><td>%R</td><td>Время в 24-часовом формате</td></tr><tr><td>%S</td><td>Секунда в десятичном формате</td></tr><tr><td>%T</td><td>Время (час:минута:секунда)</td></tr><tr><td>%u</td><td>Номер дня недели в десятичном формате</td></tr><tr><td>%U</td><td>Номер недели в году</td></tr><tr><td>%Y</td><td>Год</td></tr></tbody></table>

Строка приглашения

PS1 - основное приглашение

PS2 - при незавершенной строке

В переменной PS1

<table id="bkmrk-%D0%9A%D0%BE%D0%B4-%D0%A7%D1%82%D0%BE-%D0%BF%D0%BE%D0%BA%D0%B0%D0%B7%D1%8B%D0%B2%D0%B0%D0%B5%D1%82-%5C"><thead><tr><th>Код</th><th>Что показывает</th></tr></thead><tbody><tr><td>`\u`</td><td>Имя текущего пользователя</td></tr><tr><td>`\h`</td><td>Имя хоста (до первой точки)</td></tr><tr><td>`\H`</td><td>Полное имя хоста</td></tr><tr><td>`\w`</td><td>Текущая директория (полный путь, с `~` для home)</td></tr><tr><td>`\W`</td><td>Текущая директория (только имя последней папки)</td></tr><tr><td>`\!`</td><td>Номер текущей команды в истории</td></tr><tr><td>`\#`</td><td>Номер команды (считает все команды с момента запуска shell)</td></tr><tr><td>`\$`</td><td>Отображает `$` для обычного пользователя и `#` для root</td></tr><tr><td>`\t`</td><td>Время в формате `HH:MM:SS`</td></tr><tr><td>`\T`</td><td>Время в формате `HH:MM` (12-часовой формат)</td></tr><tr><td>`\@`</td><td>Время в формате `hh:mm AM/PM`</td></tr><tr><td>`\d`</td><td>Дата (например: `Mon Aug 26`)</td></tr><tr><td>`\n`</td><td>Перенос строки</td></tr><tr><td>`\s`</td><td>Имя используемой оболочки (обычно `bash`)</td></tr><tr><td>`\v`</td><td>Версия Bash (например: `5.2`)</td></tr><tr><td>`\V`</td><td>Версия Bash с патч-уровнем</td></tr><tr><td>`\j`</td><td>Количество фоновых задач, запущенных из shell</td></tr><tr><td>`\!`</td><td>Номер команды в истории</td></tr><tr><td>`\#`</td><td>Номер команды (считает все команды в текущей сессии)</td></tr><tr><td>`\e` или `\033`</td><td>Escape-символ (для цветов/ANSI-последовательностей)</td></tr></tbody></table>

Пример: PS1="\\u@\\h:\\w\\$ "

**PATH**

Просмотр последовательно всех путей.

Добавление в пути: PATH=$PATH":/home/you/bin"

Для ускорения поиска пути к приложениям хранятся в хэш таблице в каждой сессии.

hash -r очищает таблицу хэшей путей.

**CDPATH**

Аналог path для директорий поиска при использовании cd.

**Переменные окружения**

Вызванная программа не знает переменных консоли. Для видимости нужно экспортировать переменную в переменную окружения.

<table border="1" id="bkmrk-export-varnames-%D0%9C%D0%BE%D0%B6%D0%B5" style="border-collapse: collapse; width: 100%; height: 147.172px;"><colgroup><col style="width: 31.4882%;"></col><col style="width: 68.631%;"></col></colgroup><tbody><tr><td>env</td><td>Список переменных окружения</td></tr><tr style="height: 37.1875px;"><td style="height: 37.1875px;">export varnames</td><td style="height: 37.1875px;">Может быть список через пробел</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">export varname=value command</td><td style="height: 29.7969px;">Доступность переменной только для определенной команды</td></tr></tbody></table>

source .envfile догружает переменные из файла

Переменные по умолчанию

<table border="1" id="bkmrk-bash_version-%D0%92%D0%B5%D1%80%D1%81%D0%B8%D1%8F-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.7263%;"></col><col style="width: 78.3929%;"></col></colgroup><tbody><tr><td>BASH\_VERSION</td><td>Версия</td></tr><tr><td>BASHPID</td><td>  
</td></tr><tr><td>GROUPS</td><td>Группы текущего пользователя</td></tr><tr><td>HOSTNAME</td><td>Имя хоста</td></tr><tr><td>OSTYPE</td><td>  
</td></tr><tr><td>PWD</td><td>Текущая рабочая директория</td></tr><tr><td>RANDOM</td><td>Случайное число от 0 до 32767</td></tr><tr><td>UID</td><td>Идентификатор пользователя</td></tr><tr><td>SHELL</td><td>Полный путь к текущей консоли</td></tr></tbody></table>

**Периферийные функции**

<table border="1" id="bkmrk-sleep-n-%D0%9F%D0%B0%D1%83%D0%B7%D0%B0-%D0%BD%D0%B0-n-%D1%81" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.9781%;"></col><col style="width: 74.0219%;"></col></colgroup><tbody><tr><td>sleep n</td><td>Пауза на n секунд</td></tr><tr><td>seq 1 10</td><td>Последовательность чисел от 1 до 10</td></tr><tr><td>date '+%Y-%m-%d %H:%M:%S'</td><td>Текущая дата и время</td></tr><tr><td>tail -f fname</td><td>Выводит изменения файла в текущую консоль</td></tr></tbody></table>

# Bash скрипты

При запуске скрипта создается дочерняя консоль, в ней выполняется скрипт и результат возвращается в консоль.

Можно запускать через source, имя (если . в PATH), ./... , и ./ ... &amp; С фоновой задачей аккуратно с объемом вывода, ^C не работает.

**Shebang**

Лучше использовать shebang в начале (указатель, какую консоль использовать)

\#!/bin/bash

<table border="1" id="bkmrk-%23%21%2Fbin%2Fbash--x-%D0%92%D1%8B%D0%B2%D0%BE%D0%B4" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 20.6548%;"></col><col style="width: 79.4644%;"></col></colgroup><tbody><tr><td>\#!/bin/bash -x</td><td>Выводит все команды во время исполнения</td></tr><tr><td>\#!/bin/bash -r</td><td>Блокирует потенциально опасные действия</td></tr></tbody></table>

Параметры можно указывать при запуске скрипта, тогда shebang работать не будет.

**Удобства**

Комментарии #

bash -n script.sh проверка синтаксиса без исполнения скрипта

Внутри можно использовать следующую конструкцию для вывода части скрипта:

```
#!/bin/bash
...
set -x
...potentially error part...
set +x
```

Операция ; запускает несколько команд независимо от их статуса завершения

```
ls; ps; whoami
```

выполнит все команды

**Перенаправления результата выполнения**

<table border="1" id="bkmrk-%3E-stdout-%D0%B2-%D1%84%D0%B0%D0%B9%D0%BB%2C-%D1%84%D0%B0%D0%B9" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 18.7501%;"></col><col style="width: 81.3691%;"></col></colgroup><tbody><tr><td>&gt;</td><td>Stdout в файл, файл очищается</td></tr><tr><td>&gt;&gt;</td><td>Stdout в файл, файл дополняется</td></tr><tr><td>&amp;&gt; or &gt;&amp;</td><td>Stdout и Stderr в файл, файл очищается</td></tr><tr><td>&amp;&gt;&gt;</td><td>Stdout и Stderr в файл, файл дополняется</td></tr><tr><td>&lt;</td><td>Перенаправление ввода в команду</td></tr><tr><td>&lt;&lt;</td><td>Перенаправление многострочного ввода в команду</td></tr></tbody></table>

Номера потоков

0 - stdin, 1 - stdout, 2 - stderr

```
lzl -l 1> stdout.txt 2>stderr.txt
```

выведет в stderr.txt

**Функции**

```
function functname{
shell commands
}

functname(){
shell commands
}
```

Используют позиционные переменные.

functname "one" $7

Если внутри функции нужна локальная переменная, то local var1="something", иначе используется глобальная переменная.

Статус возврата из функции перезаписывается после каждого вызова команды, поэтому при необходимости возврата статуса некоторой функции внутри нужно сохранять статус.

```bash
cd baddir
echo $?
```

По умолчанию возвращается статус вызова последней команды. Либо через return N

```bash
builtin mkdir "/var/first"
es=$?
echo "$OLDPWD --> $PWD"
return $es

```

Желательно возвращать код результата, а не просто цифру.

**Программы для работы с функциями**

<table border="1" id="bkmrk-declare--f-%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D1%84%D1%83" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 16.7263%;"></col><col style="width: 83.3929%;"></col></colgroup><tbody><tr><td>declare</td><td>-f список функций с описанием</td></tr><tr><td>  
</td><td>-F только имена функций</td></tr><tr><td>unset fname</td><td>удалить функцию из памяти</td></tr><tr><td>type fname</td><td>Описание функции</td></tr><tr><td>  
</td><td>-t выведет тип</td></tr></tbody></table>

**Приоритет при вызове из консоли**

1. Aliases
2. Служебные слова типа if, ...
3. Функции
4. Встроенные команды типа cd, type, ...
5. Скрипты и исполняемые команды

**Переменные**

**Строковые (по умолчанию)**

Описывается $varname или ${varname} Например есть переменная UID. Для вывода 0\_ нужно использовать echo ${varname}\_

Удаление переменной unset var1

<table border="1" id="bkmrk-%241%2C-%242%2C-...-%D0%9F%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D0%BE%D0%BD" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 13.631%;"></col><col style="width: 86.4882%;"></col></colgroup><tbody><tr><td>$1, $2, ...</td><td>Позиционные параметры</td></tr><tr><td>$\*</td><td>Строка, содержащая все параметры через пробел</td></tr><tr><td>$@</td><td>Равна N строкам-параметрам. Актуально при передаче в функции.</td></tr><tr><td>$#</td><td>Количество аргументов</td></tr><tr><td>var1=$(cmd)</td><td>Сохранение результата cmd в переменную</td></tr></tbody></table>

Определение глобальной переменной: var1="something"

Обработка значений переменных перед вводом

<table border="1" id="bkmrk-%24%7Bvarname%3A-word-%7D-%D0%95%D1%81" style="border-collapse: collapse; width: 100%; height: 165.781px;"><colgroup><col style="width: 23.9882%;"></col><col style="width: 76.131%;"></col></colgroup><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">${varname:-word }</td><td style="height: 29.7969px;">Если переменная не существует или существует но пустая - вернет word</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">${varname:=word}</td><td style="height: 29.7969px;">Если переменная не существует или существует но пустая - присвоит ей word и вернет word</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">${varname:?message}</td><td style="height: 46.5938px;">Если переменная не существует или существует но пустая - выведет сообщение и завершит скрипт</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">${varname:+word}</td><td style="height: 29.7969px;">Если переменная существует или пустая - вернет word, иначе null</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">${varname:offset:length}</td><td style="height: 29.7969px;">Срез.

count=frogfootman

${count:4} вернет footman

${count:4:4} вернет foot

</td></tr></tbody></table>

Обработка переменных

<table border="1" id="bkmrk-%24%7Bvar%23pattern%7D-%C2%A0-%D0%95%D1%81%D0%BB" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 23.9882%;"></col><col style="width: 76.131%;"></col></colgroup><tbody><tr><td>${var#pattern}

</td><td>Если шаблон совпадает с началом значения переменной - удалить самую короткую часть от начала строки с совпадением и вернуть результат

var="/home/user/docs/file.txt"  
echo ${var#\*/} # home/user/docs/file.txt (отрезан только первый "/")

</td></tr><tr><td>${var##pattern}</td><td>var="/home/user/docs/file.txt"

echo ${var##\*/} # file.txt (отрезано всё до последнего "/")

</td></tr><tr><td>${variable%pattern}</td><td>file="archive.tar.gz"

echo ${file%.\*} # archive.tar

</td></tr><tr><td>${variable%%pattern}</td><td>file="archive.tar.gz"

echo ${file%%.\*} # archive

</td></tr><tr><td>${variable/pattern/string}</td><td>Наибольшее совпадение шаблона с переменной заменяется строкой. Первое совпадение

</td></tr><tr><td>${variable//pattern/ string}</td><td>Наибольшее совпадение шаблона с переменной заменяется строкой. Все совпадения

</td></tr><tr><td>  
</td><td> echo -e ${PATH//:/'\\n'}

выведет разделенное через : значение в значения списком

</td></tr></tbody></table>

Длина значения ${#filename}

**Типизированные**

При помощи declare &lt;param&gt; var

<table border="1" id="bkmrk--a-%D0%9C%D0%B0%D1%81%D1%81%D0%B8%D0%B2--i-%D0%A6%D0%B5%D0%BB%D0%BE%D0%B5-%D1%87" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 23.631%;"></col><col style="width: 76.4882%;"></col></colgroup><tbody><tr><td>-a</td><td>Массив</td></tr><tr><td>-i</td><td>Целое число</td></tr><tr><td>-r</td><td>Переменная только для чтения</td></tr><tr><td>-x</td><td>Переменная экспортируется в окружение</td></tr><tr><td>-f</td><td>Хрень. Возвращает тело функции если она есть.

```bash
myfunc() {
    echo "Hello from myfunc"
}
declare -f myfunc
```

</td></tr><tr><td>-F</td><td>Возвратит имя функции если она есть.</td></tr></tbody></table>

Пример:

```bash
declare -i val3=12 val4=5
declare -i result2
result2=val3*val
echo $result2
```

Выведет 60

Использование let

```
let result="4 * 5"
echo $result
```

Также можно использовать двойные скобки

```
let result=$((4 * 5))
echo $result
```

Есть операции +,-,/,\*,%-модуль, += увеличение на константу, -=

**Массивы**

```
arr = (1 2 3)
echo "${arr[*]}" #весь массив
echo "${arr[0]}" #1
unset ${arr[1]} #удалит 2 из массива
${arr[0]}=10 #перезапишет 1 в 10
```

**Условия**

**Условия завершения программы**

Условие анализируется не на логическом, а на статусе результата (exit status) выполнения программы. 0 - ОК, 1-255 ошибка.

```
if condition; then
statements
[elif condition; then 
statements...]
[else
statements]
fi
```

**Условие анализа строк**

 конструкция \[\], внутри нее возможны условия проверки и сравнения строк.

<table border="1" id="bkmrk-str1-%3D-str2-%D0%A1%D1%82%D1%80%D0%BE%D0%BA%D0%B0-1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.3691%;"></col><col style="width: 78.7501%;"></col></colgroup><tbody><tr><td>str1 = str2</td><td>Строка 1 равна строке 2</td></tr><tr><td>str1 != str2</td><td>Строка 1 не равна строке 2</td></tr><tr><td>str1 &gt; str2 str1 &lt; str2</td><td>Больше/меньше</td></tr><tr><td>-n str1</td><td>Строка 1 не пустая</td></tr><tr><td>-z str1</td><td>Строка 1 пустая</td></tr></tbody></table>

**Условия сравнения чисел**

<table border="1" id="bkmrk--lt-%D0%9C%D0%B5%D0%BD%D0%B5%D0%B5-%D1%87%D0%B5%D0%BC--le-%D0%9C%D0%B5" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.131%;"></col><col style="width: 78.9882%;"></col></colgroup><tbody><tr><td>-lt</td><td>Менее чем</td></tr><tr><td>-le</td><td>Меньше или равно</td></tr><tr><td>-eq</td><td>Равно</td></tr><tr><td>-ge</td><td>Больше или равно</td></tr><tr><td>-gt</td><td>Больше</td></tr><tr><td>-ne</td><td>Не равно</td></tr></tbody></table>

Можно комбинировать

```bash
if command && [ condition ]; then
```

Логика внутри условия

<table border="1" id="bkmrk-%26%26-%D0%B8%D0%BB%D0%B8--a-%D0%92%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D1%8C-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.131%;"></col><col style="width: 78.9882%;"></col></colgroup><tbody><tr><td>&amp;&amp; или -a</td><td>Выполнить команду 1, если статус 0 - выполнить команду 2

```bash
if statement1 && statement2
then
...
fi
```

</td></tr><tr><td>|| или -o</td><td>Выполнить команду 1, если статус не 0 - выполнить команду 2

```bash
if statement1 || statement2
then
...
fi
```

</td></tr><tr><td>!</td><td>Отрицание

</td></tr></tbody></table>

Свойства файла, проверяемые в условиях

<table border="1" id="bkmrk--a-file%2C--e-file-%D0%A4%D0%B0%D0%B9" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.012%;"></col><col style="width: 79.1072%;"></col></colgroup><tbody><tr><td>-a file, -e file</td><td>Файл существует</td></tr><tr><td>-d file</td><td>Директория существует</td></tr><tr><td>-f file</td><td>Файл существует и является файлом (не директория или специальный файл)</td></tr><tr><td>-r file, -w file</td><td>Есть право чтения файла, Есть право записи в файл, </td></tr><tr><td>-x file</td><td>Есть право исполнения файла или право поиска в директории</td></tr><tr><td>-s file</td><td>Файл существует и не пустой</td></tr><tr><td>-N file</td><td>Файл был изменен с момента последнего чтения</td></tr><tr><td>-O file, -G file</td><td>Запустивший скрипт является владельцем файла, Группа запустившего скрипт входит в группу файла</td></tr><tr><td>file1 -nt file2</td><td>file1 новее file2 (сравниваются время модификации)</td></tr><tr><td>file1 -ot file2</td><td>file1 старее file2 (сравниваются время модификации)</td></tr></tbody></table>

**Цикл for**

Итерируется только по полному списку.

```
for name [in list]; do
    statements that can use $name...
    done
```

Если list не определен, то используется $@

Есть переменная IFS

```bash
IFS=:
for dir in $PATH
  do
    ls -ld $dir
  done
```

break выход из цикла, continue продолжить следующую итерацию

**Цикл while**

```bash
while condition; do
statements...
done
```

Для бесконечного цикла можно использовать while true; do

Просмотр каждой строки вывода программы (например ping)

```bash
#!/bin/bash

ping 172.16.10.1 | while read -r line; do
  echo Пинг $line | awk '{print $1 $6}'
  done

```

**Цикл until**

```bash
until command; do
statements...
done

```

**Case**

```bash
for filename in "$@"; do
  pnmfile=${filename%.*}.ppm
  case $filename in
    *.jpg ) exit 0 ;;
    *.tga ) tgatoppm $filename > $pnmfile ;;
    *.xpm ) xpmtoppm $filename > $pnmfile ;;
    *.pcx ) pcxtoppm $filename > $pnmfile ;;
    *.tif ) tifftopnm $filename > $pnmfile ;;
    *.gif ) giftopnm $filename > $pnmfile ;;
    * ) echo "procfile: $filename is an unknown graphics file." 
        exit 1 ;;
  esac
  outfile=${pnmfile%.ppm}.new.jpg
  pnmtojpeg $pnmfile > $outfile
  rm $pnmfile
done
```

**Опции исполнения**

shift смещает переменные влево.

```
myshift.sh
echo $1 $2
shift
echo $1 $2
Использование: ./myshift.sh one two
Вывод:
one two
two
```

Вариант скрипта, считывающего параметры со случайным размещением параметров

```bash
while [ -n "$(echo $1 | grep '-')" ]; do
  case $1 in
    -a ) aopt=$2
         shift;;
    -b ) bopt="True"
        ;;
    -c ) copt=$2
        shift;;
    *  ) echo 'usage: alice [-a] [-b] [-c] args...'
         exit 1
  esac
  shift
done
```

Опция -b является одинарной, -a и -c параметризованные.

В процессе исполнения можно добавлять интерактивность за счет считывания аргументов

```
echo "First arg"
read -r farg
echo "First arg is ${farg}"
```

**Вызов другого скрипта**

Если есть права на исполнение и правильный shebang то вызов по имени. Скрипт исполняется в подпроцессе.

```bash
#!/bin/bash
echo "Это первый скрипт"

./script2.sh   # вызов второго скрипта
```

Если директория script2.sh не в PATH то нужен полный путь.

Для выполнения скрипта в одном процессе, все переменные передаются:

```bash
#!/bin/bash
echo "Это первый скрипт"

source script2.sh
```

Также можно передавать аргументы.

# Примеры скриптов

**Задача:** Есть файл формата кол-во альбомов TAB исполнитель. Нужно вывести первые N исполнителей. Первый аргумент имя файла, второй (необязательный) N. По умолчанию 10.

```bash
filename=${1:?'Нужно определить имя файла в первом аргументе!'}
maxcount=${2:-10}
sort -nr $filename | head -n $maxcount
```

**Задача:** Обертка для команды ls, которая выводит информацию в человекочитаемом виде.

```bash
if [ ! -e "$1" ]; then
    echo "Файл $1 не существует"
    exit 1
fi
if [ -d "$1" ]; then
    echo -n "$1 директория, в которой "
    if [ ! -x "$1" ]; then
       echo -n "нельзя "
    fi
    echo "проводить поиск."
elif [ -f "$1" ]; then
    echo "$1 обычный файл."
else
    echo "$1 специальный файл."
fi
if [ -O "$1" ]; then
    echo 'Вы владелец этого файла'
else
    echo 'Вы не являетесь владельцем этого файла.'
fi
if [ -r "$1" ]; then
    echo 'У вас есть право чтения этого файла.'
fi
if [ -w "$1" ]; then
    echo 'У вас есть право записи этого файла.'
fi
if [ -x "$1" -a ! -d "$1" ]; then
    echo 'У вас есть право исполнения этого файла.'
fi

```

**Задача:** На входе скрипта два параметра: текстовый идентификатор и адрес сайта. Если не хватает параметров - ошибка и выход. Пропинговать адрес, сохранить данные в файл в виде Идентификатор, адрес, результат пинг (Да/Нет), дата и время.

```bash
#!/bin/bash

PINGRESULT='ping_result.csv'

ident=${1:?'Нужно определить идентификатор хоста в первом аргументе!'}
myhost=${2:?'Нужно определить адрес хоста во втором аргументе!'}


if [ ! -e "$PINGRESULT" ]; then
    touch $PINGRESULT
fi

if [ -d "$PINGRESULT" ]; then
    echo "$PINGRESULT это директория. Удалите ее или измените имя файла."
        exit 1
fi

declare -i res=$(ping $myhost -c 1 | grep transmitted | awk '{print $4}')
if [ $res -eq "1" ]; then
    isaval="Доступен"
else
    isaval="Не доступен"
fi
echo ${ident},${myhost},${isaval},$(date '+%Y-%m-%d %H:%M:%S') > $PINGRESULT
```

**Задача:** Отправить сообщение пользователю telegram

```bash
#!/bin/bash
#sudo apt update && sudo apt install -y jq
#usage ./tgsender "My message"

BOT_TOKEN="..."
# 📢 ID канала или ID user
CHAT_ID="..."

MESSAGE=${1:?"Необходимо определить сообщение в первом аргументе"}

# Запрос в Telegram API
res=$(curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
     -d chat_id="${CHAT_ID}" \
     -d text="${MESSAGE}" \
     -d parse_mode="Markdown"\
     | jq -r '.ok')

if [ "${res}" = "true" ]; then
  echo "Ok"
else
  echo "Error"
fi
```

# Регулярные выражения

<div id="bkmrk-%D0%9C%D0%B5%D1%82%D0%B0%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D1%8B">**Метасимволы**</div><div id="bkmrk-%5E-%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%BE-%24-%D0%BA%D0%BE%D0%BD%D0%B5%D1%86-%5B%5D-"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.2682%;"></col><col style="width: 74.7318%;"></col></colgroup><tbody><tr><td>^</td><td>начало</td></tr><tr><td>$</td><td>конец</td></tr><tr><td>\[\]</td><td>список символов

\[1-6\] перечисление, может комбинироваться порядок роли не играет \[0-5a-fkl\]

\[- если дефис в начале, то как символ

^ Если в начале, то инверсия правила. Если не в начале, то обычный символ

</td></tr><tr><td>|</td><td>или. Желательно экранировать остальное скобками, например gr(e|a)y

внутри могут быть полноценные регулярные выражения

</td></tr><tr><td>.</td><td>Один любой символ, кроме новой строки \\n.</td></tr><tr><td>?</td><td>0 или 1 вхождение предшествующего элемента</td></tr><tr><td>+</td><td>1 и более вхождений предшествующего элемента</td></tr><tr><td>\*</td><td>0 и более вхождений предшествующего элемента</td></tr><tr><td>\\w</td><td>Любая цифра или буква

</td></tr><tr><td>\\W</td><td>все, кроме буквы или цифры</td></tr><tr><td>\\d</td><td>Любая цифра \[0-9\]</td></tr><tr><td>\\D</td><td>все, кроме цифры</td></tr><tr><td>\\xFF</td><td>шестнадцатеричное число</td></tr><tr><td>\\s</td><td>Любой пробельный символ</td></tr><tr><td>\\S</td><td>любой непробельный символ</td></tr><tr><td>\\b</td><td>Граница слова</td></tr><tr><td>\\A</td><td>начало текста</td></tr><tr><td>\\t, \\n, \\r</td><td>Символ табуляции, новой строки и возврата каретки соответственно</td></tr><tr><td>{n,m}</td><td>От n до m вхождений

{n} ровно n вхождений

{n,} от n вхождений

{,m} — от 0 до m

Иногда нужно экранировать \\{

</td></tr></tbody></table>

</div><div id="bkmrk-%D0%9C%D0%BE%D0%B4%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82%D0%BE%D1%80%D1%8B">**Модификаторы**</div><div id="bkmrk-%28%3Fi%3A%D1%87%D1%82%D0%BE-%D1%82%D0%BE%29-%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%B2%D0%B0%D0%BB"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.6548%;"></col><col style="width: 74.4644%;"></col></colgroup><tbody><tr><td>(?i:что-то)</td><td>интервальный модификатор, отключающий поиск с учетом регистра для "что-то"</td></tr><tr><td>(?:что-то)</td><td>Атомарная группировка проходит как обычно, но когда процесс поиска выходит за пределы конструкции (за закрывающую круглую скобку), все сохраненные состояния удаляются.</td></tr><tr><td>(?P&lt;word&gt;...)</td><td>Именованная группа, в данном случае имя - word

m.group('word') - пример обращения к группе

</td></tr></tbody></table>

</div><div id="bkmrk-%D0%9E%D0%BF%D0%B5%D1%80%D0%B5%D0%B6%D0%B0%D1%8E%D1%89%D0%B0%D1%8F-%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%BA%D0%B0">**Опережающая проверка**</div><div id="bkmrk-%28%3F%3C%3D...%29-%D0%94%D0%BE%D0%BB%D0%B6%D0%BD%D0%BE-%D1%81%D0%BE%D0%B2%D0%BF"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 26.012%;"></col><col style="width: 74.1072%;"></col></colgroup><tbody><tr><td>(?&lt;=...)</td><td>Должно совпасть слева (Позитивная ретроспективная проверка), в Python - fixed length. </td></tr><tr><td>(?&lt;!...)</td><td>Не должно совпасть слева (Негативная ретроспективная проверка), в Python не поддерживается.</td></tr><tr><td>(?=...)</td><td>Должно совпасть справа (Позитивная опережающая проверка).</td></tr><tr><td>(?!...)</td><td>Не должно совпасть справа (Негативная опережающая проверка).</td></tr><tr><td>\\g&lt;...&gt;</td><td>группы при обратных ссылках, g&lt;1&gt; группа 1 </td></tr></tbody></table>

</div><div id="bkmrk-"></div><div id="bkmrk--1"></div><div id="bkmrk-.%2A%3F%C2%A0-%22%D0%BB%D0%B5%D0%BD%D0%B8%D0%B2%D1%8B%D0%B9%22-%D0%BF%D0%BE%D0%B8%D1%81%D0%BA">.*? "ленивый" поиск</div><div id="bkmrk-%D0%90%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D1%8B">Атрибуты</div><div id="bkmrk-%5Cp%7B%D0%B0%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%7D">\p{атрибут}</div><div id="bkmrk-%5Cp%7Bl%7D-%5Cp%7Bletter%7D-%E2%80%93-%D1%81">\p{L} \p{Letter} – символы, считающиеся буквами</div><div id="bkmrk-%5Cp%7Bm%7D-%5Cp%7Bmark%7D-%E2%80%93-%D1%80%D0%B0%D0%B7">\p{M} \p{Mark} – различные символы, существующие не самостоятельно, а лишь в сочетании с другими базовыми символами (диакритические знаки, рамки и т. д.)</div><div id="bkmrk-%5Cp%7Bz%7D-%5Cp%7Bseparator%7D-">\p{Z} \p{Separator} – символы, выполняющие функции разделителей, но не имеющие собственного визуального представления (разнообразные пробелы и т. д.)</div><div id="bkmrk-%5Cp%7Bs%7D-%5Cp%7Bsymbol%7D-%E2%80%93-%D1%80">\p{S} \p{Symbol} – различные декоративные элементы и знаки</div><div id="bkmrk-%5Cp%7Bn%7D-%5Cp%7Bnumber%7D-%E2%80%93-%D1%86">\p{N} \p{Number} – цифры</div><div id="bkmrk-%5Cp%7Bp%7D-%5Cp%7Bpunctuation">\p{P} \p{Punctuation} – знаки препинания</div><div id="bkmrk-%5Cp%7Bc%7D-%5Cp%7Bother%7D-%E2%80%93-%D0%BF%D1%80">\p{C} \p{Other} – прочие символы (редко используется при работе с обычным текстом)</div><div id="bkmrk-%D0%9F%D0%BE%D1%85%D0%BE%D0%B6%D0%B5%2C-%D1%87%D1%82%D0%BE-python-%D0%BD">Похоже, что python не поддерживает</div><div id="bkmrk--2">  
</div><div id="bkmrk--3">  
</div><div class="align-center" id="bkmrk-python">**Python**</div><div id="bkmrk-%D0%A4%D0%BB%D0%B0%D0%B3%D0%B8-%D0%BF%D1%80%D0%B8-%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5">Флаги при обработке</div><div id="bkmrk-re.compile%28%27...%27%2C-re">re.compile('...', re.IGNORECASE) - игнор регистра</div><div id="bkmrk-re.verbose-re.x-allo">re.VERBOSE re.X Allow inline comments and extra whitespace. </div><div id="bkmrk-re.ignorecase-re.i-d">re.IGNORECASE re.I Do case-insensitive matches.</div><div id="bkmrk-re.multiline-re.m-al">re.MULTILINE re.M Allow anchors (^ and $) to match the beginnings and ends of lines instead of matching the beginning and end of the whole text.</div><div id="bkmrk-re.dotall-re.s-allow">re.DOTALL re.S Allow dot (.) to match any character, including a newline. (The default behavior of dot is to match anything, except for a newline.)</div><div id="bkmrk--4">  
</div><div id="bkmrk-%D0%9D%D1%8E%D0%B0%D0%BD%D1%81%D1%8B-python">Нюансы python</div><div id="bkmrk-%D0%92-%D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%89%D0%B0%D0%B5%D0%BC%D0%BE%D0%BC-%D0%BA%D0%BE%D1%80%D1%82%D0%B5">В возвращаемом кортеже группа 0 - максимальная группа. Группа определяется скобками. Поэтому если внутри есть () для формирования регулярки, то необходимо всю регулярку брать в скобки.</div><div id="bkmrk-%D0%92%D0%BC%D0%B5%D1%81%D1%82%D0%BE-r%27gr%28e%7Ca%29y%27-%D0%BC">Вместо r'gr(e|a)y' можно использовать f-строки, {} будет означать переменную</div><div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%3A%C2%A0">Пример: </div><div id="bkmrk-m-%3D-re.finditer%28r%27gr">m = re.finditer(r'gr(e|a)y', 'gray grey')</div><div id="bkmrk-%C2%A0-%C2%A0-for-ma-in-m%3A"> for ma in m:</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-print%28ma.gro"> print(ma.groups())</div><div id="bkmrk-%23%28%27a%27%2C%29">\#('a',)</div><div id="bkmrk-%23%28%27e%27%2C%29">\#('e',)</div><div id="bkmrk-m-%3D-re.finditer%28r%27%28g">m = re.finditer(r'(gr(e|a)y)', 'gray grey')</div><div id="bkmrk-%C2%A0-%C2%A0-for-ma-in-m%3A-1"> for ma in m:</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-print%28ma.gro-1"> print(ma.groups())</div><div id="bkmrk-%23%28%27gray%27%2C-%27a%27%29">\#('gray', 'a')</div><div id="bkmrk-%23%28%27grey%27%2C-%27e%27%29">\#('grey', 'e')</div><div id="bkmrk--5">  
</div><div id="bkmrk-%D0%9F%D0%BE-%D1%83%D0%BC%D0%BE%D0%BB%D1%87%D0%B0%D0%BD%D0%B8%D1%8E-%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D1%8B">По умолчанию символы повторения жадные (greedy). Если нужно отключить жадность, достаточно добавить знак вопроса после символов повторения: match = re.search(r'&lt;.*?&gt;', line)</div><div id="bkmrk--6">  
</div><div id="bkmrk-re.sub%28pattern%2C-repl">re.sub(pattern, repl, string, count=0) Заменить в строке string все непересекающиеся шаблоны pattern на repl</div>

# Grep, awk, sed

Общий синтаксис для sed и awk: command \[options\] script filename

Если скрипт определяется в команде, то одинарные кавычки.

-f filename имя скрипта

Каждая инструкция скрипта состоит из блока шаблона и процедуры. Шаблон отделяется /. Строки скрипта интерпретируются последовательно, если найдено соответствие шаблону - исполняется процедура. Применяется каждая строка скрипта. Sed выводит строку, поведение awk нужно определять в скрипте. В скриптовом файле шаблон пишется без кавычек

```
cat sedscr
s/ MA/, Massachusetts/
s/ PA/, Pennsylvania/
s/ CA/, California/
s/ VA/, Virginia/
s/ OK/, Oklahoma/
```

**grep**

Регулярки пишутся в двойных кавычках

grep "\[PV\]A" testone.txt

grep "10\\{1,2\\}1" testone.txt

grep по умолчанию не понимает спецсимволы типа \\d,... \\d интерпретируется как символ d. Чтобы понимал:

```bash
echo "123 abc" | grep -P '\d'
```

<table border="1" id="bkmrk-%D0%9E%D0%BF%D1%86%D0%B8%D1%8F-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5--e-%D0%9C%D0%BD" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Опция</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>-P</td><td>Регулярка в стиле Perl

</td></tr><tr><td>-o</td><td>Показать только совпадения

</td></tr></tbody></table>

**sed**

Правила применяются последовательно, поэтому одно правило может сделать строку, соответствующую второму правилу. И результат замены может быть неожиданным.

<table border="1" id="bkmrk-%D0%9E%D0%BF%D1%86%D0%B8%D1%8F-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5--e-%D0%9C%D0%BD-1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Опция</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>-e</td><td>Многострочный скрипт в командной строке,

sed -e ’s/ MA/ Massachusetts/’ -e ’s/ PA/ Pennsylvania/’ list

Но проще через ;

sed ’s/ MA/ Massachusetts/; s/ PA/ Pennsylvania/’ list

</td></tr></tbody></table>

Правила для скрипта

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%BE-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-s-%D0%97" style="border-collapse: collapse; width: 100%; height: 275.766px;"><colgroup><col style="width: 33.4826%;"></col><col style="width: 66.5174%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">Правило</td><td class="align-center" style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 80.1875px;"><td style="height: 80.1875px;">s</td><td style="height: 80.1875px;">Замена, файл меняется

sed 's/ MA/ Massachusetts/' testone.txt

<span style="color: rgb(224, 62, 45);">! случай 's/ MA/, Massachusetts/' не сработал, получилось без запятой. Какой-то нюанс.</span>

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">-n 's/.../.../p'</td><td style="height: 29.7969px;">Выводится только соответствующие шаблону, файл не меняется.

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">'s/.../.../g'</td><td style="height: 29.7969px;">Все вхождения в строке

</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">'s/что/на что/число'</td><td style="height: 46.5938px;">Заменить каждое вхождение с номером Число.

's/foo/bar/2' - второе вхождение foo

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">sed 's/что/на что/w имяфайла'</td><td style="height: 29.7969px;">сохранить после замены в имяфайла

</td></tr><tr><td>/Sebastopol/s/CA/California/g</td><td>Замена CA на California если есть слово Sebastopol

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">'/.../d'</td><td style="height: 29.7969px;">Удаление

</td></tr></tbody></table>

**awk**

Интерпретирует каждую строку как запись и каждое слово как поле. Переменные

$0 полная строка

$1, $2, ... номера полей. <span style="color: rgb(224, 62, 45);">Вне awk скрипта $ - параметр bash!</span>

Различные форматы применяются последовательно, например

```bash
awk '/VA/ { print $1}' testone.txt
```

Для строк, в которых есть VA, будет выведено первое поле.

Можно создавать переменные и затем использовать их в сравнении. Переменные создаются внутри {}, используются вне. Изначально все переменные - пустые строки, автоматически инициализируются.

```bash
{LastState = $1}
```

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5--f" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Параметр</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>-F</td><td>Разделитель, например

awk -F, '{ print $1}' testone.txt

</td></tr><tr><td> </td><td> </td></tr></tbody></table>

Правила для скрипта формата {}

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%BE-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-pri" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Правило</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>print</td><td>Вывод данных

awk '{ print $1 }' testone.txt

awk '{ print "Name: " $1}' testone.txt

</td></tr><tr><td>;</td><td>Разделитель команд внутри

awk '{ print $1; print $2 }' testone.txt

</td></tr><tr><td>  
</td><td></td></tr></tbody></table>

Правила для скрипта формата //

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%BE-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-%2Fso" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Правило</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>/some/</td><td>Поиск с выводом соответствия шаблону

awk ’/some/’ list

</td></tr><tr><td> </td><td> </td></tr></tbody></table>

Пример совмещения задач

```bash
! /bin/sh
awk -F, '{print $4 ", " $0}' $* |
sort |
awk -F, '
$1 == LastState {print "\t" $2}
$1 != LastState {LastState = $1; print $1; print "\t" $2}
'
```

Вывод

```
 CA
         Amy Wilde
 Massachusetts
         Eric Adams
         John Daggett
         Sal Carpenter
 OK
         Orville Thomas
 PA
         Terry Kalkas
 VA
         Alice Ford
         Hubert Sims
```