Программы
Операционные системы. Linux. Bash && "продвинутый" Bash

Операционные системы. Linux. Bash && "продвинутый" Bash

bash, regexp, printf и другие интересности

Оговорюсь заранее:
Каждая из заметок – моя шпаргалка на пару, чтобы не забыть, что по плану нужно рассказать (по сути – тезисы) + расшифровка, чтобы было понятно и неподготовленному читателю. Так что будьте аккуратны – с каждой заметкой вы становитесь чуть ближе к высшему образованию в областях "компьютерные науки" и "компьютерная безопасность".

До начала пары вспомнили chmod, немного поговорили про sudo и chown:

Изображение Шпаргалка по командам Linux, FreeBSD и MacOS

sudo

sudo (англ. substitute user and do, дословно «подменить пользователя и выполнить») — программа для системного администрирования UNIX-систем, позволяющая делегировать те или иные привилегированные ресурсы пользователям с ведением протокола работы.

tldr: получить привелегии root (администратора системы).

Глянули /etc/passwd — список учёток.

Глянули /etc/sudoers — настройки использования sudo.

ДЗ: без использования утилит id, group написать утилиту вывода групп юзера. +5 баллов в карму.

chown

Команда смены владельца файла.

Чтобы сменить владельца файлов в директории и во всех поддиректориях (рекурсивно), выполните:

sudo chown -R user:group /home/user/path/to/directory/

Менять пользователя может только root, -R — рекурсивно.

Linux, bash. Однострочники

Собственно, начало пары.

|, &&, ||

Уже разбирали. Повторяем.

| — перенаправление вывода одной команды на вход другой. Пример:

man bash | less

|| — ленивое логическое "или". Используется для выполнения операции, если предыдущая завершилась с ошибкой:

cd /root || echo Доступ запрещён

&& — ленивое логическое "и". Используется для выполнения операции, если предыдущая завершилась успешно:

cd $HOME && echo Чуи, мы дома!

cat, head, tail, grep

cat — конкатенация содержимого файлов и вывод:

cat ~/.bash* | less

head — вывести начало файла (по умолчанию 10 строк):

head /etc/passwd

tail — вывести конец файла (по умолчанию 10 строк):

tail /var/log/syslog

— полезно для чтения логов — там как раз последнее — самое интересное. Также часто используется:

tail -f /var/log/syslog

— выводить по мере поступления новых строк в файл.

grep — великая утилита для фильтрации входного потока:

cat /etc/passwd | grep root
grep root /etc/passwd  # есть вариант указать в аргументах файлы

ДЗ: пишем anti-head-tail — 2 аргумента: с какой по какую строку отправлять на вывод. +5 баллов в карму.

xargs

Отдельная заметка про xargs.

Если коротко:

ls | xargs file  # передать спиок вывода ls аргументом утилите file.

# Склеить строки
echo "a
b
c" | xargs
a b c

regexp

Oh, shi~~

Короче, есть главная проблема программирования — "придумать название переменной".

За ней ровным строем идут регулярные выражения/грамматики, инвалидация кеша и реляционная алгебра.

Я вам не скажу за главную проблему, но за регулярные выражения немного поясню.

Мы уже встречались с прекрасными подстановками типа * и, например, *.txt. Видели забавные mkdir -p ./test/{a,s,d/{q,w,e}}, но что, если я скажу, что подобным образом можно искать?!

Как-то мы уже использовали grep для поиска подстроки в строке:

grep roo /etc/passwd

— ищем "roo" в файле passwd. Но это не так круто, как искать по шаблону!

grep '^root:' /etc/passwd

— находим запись пользователя root.

Отмечу, что в каком-то виде regexp (regular expressions) есть практически во всех языках программирования, поэтому мозголомка ниже будет полезна. Понимание regexp сродни пониманию сложения — рассказывать об этом также сложно, ибо уже не помнишь, в чём проблемы восприятия (поэтому жду вопросов).

Регулярные выражения содержат 3 базовых возможности:

  1. Конкатенация (вспоминаем cat) — два выражения могут идти одно за другим. Полученное большое выражение будет соответствовать входной строке тогда и только тогда, когда часть входа, соответствующая первому маленькому выражению, сразу же следует за частью, которая соответствует второму маленькому выражению.

    ab

  2. Объединение (операция or / ||) — большое выражение соответствует строке, соответствуйщей одному из маленьких выражений, содержащихся в нём.

    a|b

  3. Замыкание — маленькое выражение может быть «повторено» ноль или более раз, чтобы соответствовать входу.

    a*

Примеры регулярных выражений

Конкатенация трёх выражений "f", "oo|ee", "t". Само же выражение "oo|ee" — объединение выражений "oo" и "ee":

f(oo|ee)t  # соответствуют foot или feet

Закрытие/замыкание/кложура и тд:

a+     # соответствует "a" один или более раз
a*     # соответствует "a" 0 или более раз
a?     # соответствует "a" 0 или 1 раз
a{2,5} # сооветствует от 2 до 5 раз "a"
a{2}   # 2 раза
a{2,}  # от 2 раз
a{,5}  # до 5 раз

Делаем одно и то же разными способами:

a(0|1|2|3|4|5|6|7|8|9)  # соотвествует a0, a1 ... a9
a[0-9]  # то же самое, что и a(0|1|2|3|4|5|6|7|8|9), но используя класс символов
a[[:digit:]]  # то же самое, что выше, но с альтернативным синтаксисом
a\\d  # то же самое, но через "сокращённое написание"

Полезно знать:

^   # символ начала строки
$   # символ конца строки (не путать с \n — переводом строки)
.   # любой символ

Помните [[:digit:]]? Такого много:

[:alnum:]   [:cntrl:]   [:lower:]   [:space:]
[:alpha:]   [:digit:]   [:print:]   [:upper:]
[:blank:]   [:graph:]   [:punct:]   [:xdigit:]

С чем это едят:

[[ "sad day" =~ (sad|happy) ]] && echo "Что-то о настроении"
grep '^root:' /etc/passwd  # запись пользователя root
awk '/false$/ {print $0}' /etc/passwd  # найти всех, кто логинится в false
cat /etc/passwd | sed '/ *#/d; /^ *$/d'  # убрали комментарии из /etc/passwd и вывели

И это только базовое. Советую читать PCRE для понимания всего ужаса используемых в реальной жизни регулярок.

Advanced bash

[[ > [

[ (команда "test") и [[ (новый тест) используются для вычисления выражений. [[ работает только в Bash, Zsh и Korn shell. Также он более мощный. [ доступен в POSIX shells.

#POSIX
[ "$variable" ] || echo 'переменная не задана или пуста'
[ -f "$filename" ] || printf 'Файла (обычного) нет: %s\n' "$filename"

if [[ ! -e $file ]]; then
    echo "Файла нет, лидо он не доступен: $file"
fi

if [[ $file0 -nt $file1 ]]; then
    printf 'файл %s новее %s\n' "$file0" "$file1"
fi

Короче говоря, в bash мы используем [[ ]] — он новее, быстрее и т.д. Если имеем какую-нибудь старую POSIX-совместимую Unix оболочку, то используем [ ]. По большей части синтаксис [[ ]] и [ ] схож.

# Унарные операции
[[ -d $file ]]    # файл типа директория
[[ -e $file ]]    # файл любого типа (проверка на существование (exists))
[[ -f $file ]]    # обычный файл
[[ ! -f $file ]]  # не (обычный файл). "!" - отрицание
[[ -z $var ]]     # истина, когда переменная пуста (zero)
[[ -n $var ]]     # ложь  , когда переменная пуста (nonzero)
[[ -r $file ]]    # файл существует и доступен для чтения
[[ -w $file ]]    # файл существует и доступен для запись
[[ -x $file ]]    # догадайтесь сами

# Сравнение строк (лексикографический порядок)
[[ a    <  b   ]]
[[ dog  >  cat ]]
[[ sh   =  sh  ]]
[[ bash != sh  ]]

# Сравнение чисел
[[ 5   -lt 10 ]]  # less than
[[ 100 -gt 99 ]]  # greater than
[[ 5   -eq 05 ]]  # equal
[[ 50  -ne 05 ]]  # not equal

# Логические операции
[[ -n $var && -f $var ]]  # переменная определеня и есть файл, указанный в ней.
[[ $(pwd) == $HOME || $(pwd) == '/' ]]  # мы в директории $HOME или в корне.

# Сравнение с шаблоном
[[ $(pwd) == $HOME/* ]]  # мы в поддиректории домашней папки или в ней

# Регулярные выражения
[[ $(date) =~ ^пятница ]] && echo Сегодня пара по ОСям

printf

printf — везде!

Его можно найти в bash, C, C++, python, perl, php, ...

По сути — форматированный вывод. Рассмотрим варианты шаблонов вывода:

%c Символ
%d Десятичное целое число со знаком
%i Десятичное целое число со знаком
%e Научный формат (строчная буква е)
%Е Научный формат (прописная буква Е)
%f Десятичное число с плавающей точкой
%g В зависимости от того, какой формат короче, применяется либо %e, либо %f
%G В зависимости от того, какой формат короче, применяется либо %E, либо %f
%o Восьмеричное число без знака
%s Строка символов
%u Десятичное целое число без знака
%x Шестнадцатеричное число без знака (строчные буквы)
%X Шестнадцатеричное число без знака (прописные буквы)
%% Знак %

Хех, не всё так просто!

  • Например, спецификатор %05d заполнит нулями пустующие позиции поля вывода, если количество цифр в целом числе, подлежащем выводу, будет меньше пяти.
  • Например, форматный код %10.4f выведет на экран число, у которого количество цифр не превышает 10, четыре из которых размещаются после десятичной точки.
  • На­пример, спецификатор %-10.2f выравнивает число с двумя знаками после точки по левому краю поля, состоящего из 10 позиций.

Немного примеров:

printf "%s\n" "Hello world"  # Вывести строку с переводом строки
printf "%.2f рублей" 2,3333333  # "2,33 рублей"
printf "\e[1;34m%.3d\e[0m\n" 42  # Вывести жирным синим цифры

Не успели. Всё же 2 пары за одну... Успели бы без ответов на вопросы. Но это не наш путь!

  • awk
  • раскраска вывода
  • sed

ДЗ

Проверю через пару — буду предвзят, требовать странное. Пройдусь по всем, у всех посмотрю.

Кто хочет — может в конце этой показать.

По результатам буду проставлять успеваемость за полусеместр.