Базовые команды
Хотя утилиты создавались разными разработчиками, значительная их часть объединена в пакет GNU coreutils — набор инструментов для работы с оболочкой, файловой системой и текстом. Ниже разберём базовые команды.
Вывод текста
Команда echo уже не раз использовалась, но не была представлена формально. Она нужна для вывода чего‑либо в стандартный поток. В нашем случае это терминал.
Команда выводит текст, включая переменные и/или выводы других команд. Также можно добавлять управляющие последовательности, перенос строки \n, табуляцию \t или звуковой сигнал \a.
$ echo -e "Username:\t$USER\nCurrent dir:\t$(pwd)\a"
Username: oliver
Current dir: /home/oliver
Если поддерживает терминал, то и Unicode‑символы:
$ echo -e '42 \u00b5A'
42 µA
Для сложного форматирования лучше использовать printf. Если вам эта функция знакома из Си, то у меня хорошие новости: у данной утилиты похожий синтаксис! Например:
$ printf "Success: %s\nIssues: %s\n" "Yes" "No"
Success: Yes
Issues: No
printf -v RESULT "Save result into a variable!"
$ echo "$RESULT"
Save result into a variable!
$ printf "Decimal:\t%d\nHex:\t\tx%02X\nOctal:\t\t0%o\n" 10 10 10
Decimal: 10
Hex: x0A
Octal: 012
$ printf "\u03c0 is %.3f\n" 3.1415926
π is 3.142
Работа с файловой системой
Используйте утилиту lsblk, чтобы посмотреть какие диски есть в системе:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 465.8G 0 disk
└─sda1 8:1 0 465.8G 0 part /home/oliver/ext_hdd
nvme0n1 259:0 0 238.5G 0 disk
├─nvme0n1p1 259:1 0 1G 0 part /boot/efi
└─nvme0n1p2 259:2 0 237.4G 0 part /
Ключ -f добавит к выводу тип файловой системы блочных устройств.
Все накопители, кроме корневого диска (с него вы уже загрузились), должны быть примонтированы в систему. На настольных дистрибутивах с графической средой обычно работает автомонтирование: вставили флешку в USB-порт — она появилась в системе. В серверных установках, где доступна в основном консоль, автомонтирования, как правило, нет — монтируют вручную.
Монтировать можно не только диски, но и директории.
В Ubuntu Desktop съёмные носители по умолчанию появляются в каталоге /media/<имя_пользователя>/<метка>. В других сценариях часто используют /mnt. Все блочные устройства видны в /dev/ (например, sdX для SATA-устройств):
$ ls /dev/sd*
/dev/sda /dev/sda1
Монтировать можно в любую другую точку:
$ sudo mount /dev/sda1 /home/oliver/ext_hdd
Завершая работу, нужно отмонтировать устройство:
$ sudo umount /home/oliver/ext_hdd
Когда вы сохраняете файл на диск, операционная система может на самом деле его туда не записать, а закэшировать на какое-то время. При размонтировании диска операция будет завершена принудительно, поэтому важно корректно отключать диск. Узнать кто сейчас работает с диском можно через утилитуlsof, а принудительно завершить запись кэшей через утилитуsync.
Автомонтирование диска при старте системы описывается в файле /etc/fstab. Добавим NTFS диск с меткой (назывнием) storage:
# in the end of the file
LABEL=storage /home/oliver/ext_hdd auto nofail,nosuid,nodev,rw,relatime,x-gvfs-show,uid=1000,gid=1000 0 0
Подробно прочитать что значит каждое поле вы можете в man 5 fstab.
Узнать количество свободного места на дисках можно через утилиту df:
$ df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 1.6G 2.2M 1.6G 1% /run
/dev/nvme0n1p2 233G 11G 210G 5% /
tmpfs 7.7G 55M 7.6G 1% /dev/shm
tmpfs 5.0M 8.0K 5.0M 1% /run/lock
efivarfs 246K 123K 119K 51% /sys/firmware/efi/efivars
/dev/nvme0n1p1 1.1G 6.2M 1.1G 1% /boot/efi
tmpfs 1.6G 120K 1.6G 1% /run/user/1000
/dev/sda1 932G 128M 932G 1% /media/oliver/storage
В выводе df -h вы могли заметить разделы tmpfs. Это области в оперативной памяти. Их можно монтировать как обычные каталоги — полезно для временных файлов и/или ускорения сборок программ: SSD меньше изнашивается, а операции быстрее.
# RAM memory
tmpfs /home/oliver/build tmpfs rw,size=512M 0 0
Она же подскажет сколько inode хранящихся на диске.
$ df -i /dev/nvme0n1p2
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p2 15564800 174392 15390408 2% /
Утилиту du подскажет куда уходит дисковое пространство.
# /home/oliver/ext_hdd
$ du -sh *
9.5G ISO
138G Photos
Используя флаги можно указать какую именно директорию исследовать и как глубоко проникнуть.
$ du -h -d 2 /home/oliver/ext_hdd
9.5G /home/oliver/ext_hdd/ISO
26G /home/oliver/ext_hdd/Photos/processed
80G /home/oliver/ext_hdd/Photos/RAW
23G /home/oliver/ext_hdd/Photos/old
138G /home/oliver/ext_hdd/Photos
Для поиска по файловой системе есть две команды. find классическим образом проходит по дереву директорий. Пример:
$ find /home/oliver/highway/data -type f -name "*.bin"
/home/oliver/highway/data/right.bin
/home/oliver/highway/data/left.bin
Поиск можно производить и по метаданным. Нужны файлы измененые за последние несколько часов? Легко.
# Files modified in the last 7 days
$ find /home/oliver/highway/data -name "*.mrs" -mtime -7
/home/oliver/highway/data/quote.mrs
find позволяет выполнять другие произвольные команды. Например, скопируем все документы *.odt в новую локацию.
$ find ./reports -name "*.odt" -exec cp {} ./backups/ \;
Утилита locate работает иначе. Поиск происходит по базе данных индексированных файлов. Индексация происходит периодически, но за-то результат можно получить почти моментально:
$ locate [filename]
Обновить базу принудительно можно утилитой updatedb.
Если утилиты нет в системе, добавьте её через пакетный менеджер:
$ sudo apt update
$ sudo apt install plocate
Встроенная утилиту cd позволит перемещаться между директориями.
# Go to the /etc directory
$ cd /etc
# Go to home (/home/oliver) directory
$ cd ~
# Return to previous directory
$ cd -
# Attempt to enter the root directory as a normal user
$ cd /root
bash: cd: /root/: Permission denied
Пути бывают абсолютные и относительные. Например, ~/highway/data/dudes.tsv — абсолютный, т.к. он идёт от корня (/). Для выражения относительного используйте сочетание — ./. Перейдём из домашней директории в папку с нашими файлами:
$ cd ./data
# or
$ cd data
Вернуться в родительскую директорию можно так:
$ cd ..
# or even go to another directory through the parent
$ cd ../chsm
ls покажет содержвание директории:
$ ls -la
total 20
drwxr-xr-x 3 oliver oliver 4096 Aug 24 18:04 .
drwxr-x--- 21 oliver oliver 4096 Aug 24 17:59 ..
-rwxr-xr-x 1 oliver oliver 1003 Jan 29 2025 Makefile
-rwxr-xr-x 1 oliver oliver 76 Jan 29 2025 README.md
drwxr-xr-x 2 oliver oliver 4096 Jan 29 2025 src
Добавив параметр -h, вывод размера будет производиться в человекочитаемых единицах, например в Мб:
-rw-r--r-- 1 oliver oliver 4.2M May 15 02:07 rick_astley.mp3
Создать пустой файл можно командой touch:
$ cd ~
$ touch file.txt
Однако, главная функция не в этом. Утилита создана для изменения метаданных файлов, в частности меток модификации и доступа. Хотите получить файл долёкого прошлого?
$ touch -t 192703230000 not_sure.txt
mv поможет перемещать и переименовывать файлы.
$ mv [/old/path/to/file/or/dir] [/new/path]
В случае, если название не меняется, то можно опустить его во второй части:
$ mv ~/highway/data/quote.mrs ~/
Команда cp копирует файлы и директории.
$ cp ~/quote.mrs ~/highway/data
Абсолютный путь текущей директории можно получить через утилиту pwd:
$ pwd
/home/oliver/highway/chsm
Утилита dirname из абсолютного пути до файла вытаскивает путь до её родительской директории. Если передать в качестве параметра саму эту директорию, то можно получить имя родительской:
$ cd src
$ dirname "${PWD}/main.c"
/home/oliver/highway/chsm/src
$ dirname $(pwd)
/home/oliver/highway/chsm
Команда basename вернёт имя текущей директории:
$ basename "$PWD"
src
Удаление файлов и директорий осуществляется командой rm:
# Delete file
rm ~/quote.mrs
# Delete directory, all subdirectories and all files
rm -rf ./build
Прича воизицах, командой rm можно удалить сам Linux. В современных дистрибутивах реализация имеет защиту от дурака, вам придётся быть более настойчивым.
# Never do like that!
$ rm -rf /
rm: it is dangerous to operate recursively on '/'
rm: use --no-preserve-root to override this failsafe
За создание директорий отвечает утилита mkdir:
# Create an empty directory
$ mkdir some_dir
# If you need to create several nested directories at once
$ mkdir -p several/nested/dirs
# You can create a directory immediately with the required access rights
$ mkdir -m a=rwx some_dir
Пустые директории можно удалять командой rmdir.
Последняя команда, которая может оказаться полезной в некоторых случаях mktemp — утилита создающая временный файл или директорию с уникальным именем. Часто используется в скриптах для того, чтобы случайно не затереть существующие файлы или директории.
# Let's create a temporary file
$ mktemp
/tmp/tmp.ZxkySUlPiF
# Let's create a temporary directory
$ mktemp -d
/tmp/tmp.qBQGligqb4
По-умолчанию файл и директория создаётся в \tmp, но вы можете указать путь и даже шаблон, а возвращаемое значение сохранить в какую-нибудь переменную для дальнейшей работы:
$ VAR=$(mktemp debug.XXXXXXXXXX)
$ echo $VAR
debug.Jz43IWRAUU
Работа с текстом
Согласно философии Linux вывод должен представляться в виде строк, пригодных и для обработки другими программами, при этом в читаемом виде. Такой подход делает командную строку универсальным инструментом для решения задач. По умолчанию вместе с дистрибутивом поставляется множество утилит для работы с текстом. Мы рассмотрим лишь часть из них; способы комбинирования покажем в следующей главе.
Статистику по файлу предоставляет утилита wc: количество строк (-l), слов (-w), символов (-m) и байт (-c).
Для подсчёта слов можно использовать, например, так:
$ wc --words chsm/README.md
14 chsm/README.md
Чтобы вывести содержимое файла в стандартный вывод, используйте cat (от concatenate). Добавьте ключ -n, чтобы вывести номера строк.
$ cat -n chsm/README.md
1 # CHSM — Checksum
2
3 A small CLI util to calculate different types of CRCs.
Для нумерации строк так же можно использовать команду nl, она более гибкая.
Это не единственная функция данной команды; можно объединять файлы:
$ cat data/dudes.tsv data/years.tsv
1 Galois
# ...
1 1811
# ...
Для вывода в обратном порядке используйте tac.
Можно вывести первые строки с начала или конца файла:
$ head -n2 data/dudes.tsv
1 Galois
2 Abel
$ tail -n3 data/dudes.tsv
3 Uhlenbeck
4 Srinivasa
5 Stefan
sort позволит отсортировать вывод (ключ -r выводит в обратном порядке). Можно даже указать колонку.
$ sort -k 2 -n data/years.tsv
6 1777
2 1802
1 1811
4 1887
5 1892
3 1942
Команда cut позволяет вытаскивать определённые секции из каждой строки.
$ cat data/elements.tsv
H 1 Hydrogen nonmetal gas
# ...
$ cut -d$'\t' -f 2 data/elements.tsv
1
2
3
5
8
13
# ...
Либо можно вытащить всё кроме указанного столбца:
$ cut -d$'\t' -f 2 --complement data/elements.tsv
H Hydrogen nonmetal gas
# ...
Только третьи символы каждой строки:
$ cut -c 3 data/dudes.tsv
G
A
U
S
S
Или даже байты (-b), если мы работаем с бинарным файлом.
Команда paste объединяет неколько входов и отправляет в стандартный вывод их комбинацию. Часто используется с cut. Создадим две колонки разделённые табом:
$ paste data/elements.tsv data/iso_mass.tsv
H 1 Hydrogen nonmetal gas 1.008
He 2 Helium noble_gas gas 4.0026
Li 3 Lithium alkali_metal solid 6.94
B 5 Boron metalloid solid 10.81
O 8 Oxygen nonmetal gas 15.999
Al 13 Aluminium post_transition_metal solid 26.982
Sc 21 Scandium transition_metal solid 44.956
Se 34 Selenium nonmetal solid 78.97
Разделитель задаётся ключом -d:
$ paste -d "," data/elements.tsv data/iso_mass.tsv
Команда seq используется для создания последовательности чисел по определённому шаблону. Используем её как ввод:
$ seq 1 9 | paste - - -
1 2 3
4 5 6
7 8 9
column удобна при выводе текста в виде колонок, при этом выравнивание произойдёт автоматически:
$ cat data/elements.tsv
H 1 Hydrogen nonmetal gas
He 2 Helium noble_gas gas
Li 3 Lithium alkali_metal solid
# ...
$ column -t -R 1 data/elements.tsv
H 1 Hydrogen nonmetal gas
He 2 Helium noble_gas gas
Li 3 Lithium alkali_metal solid
# ...
Получить только уникальные записи поможет утилита uniq:
# [print also the number of mentions]
$ uniq [-c] data/numbers.txt
Имейте ввиду, команда uniq уберёт следидущие повторяющиеся символы, т.е. если вы хотите получить уникальные значения по всему вводу, то его нужно сначала отсортировать.
Сравнить файлы бинарно может cmp:
$ cmp data/right.bin data/left.bin
data/right.bin data/left.bin differ: byte 13, line 1
diff работает с текстом:
$ diff data/right.bin data/left.bin
1c1
< 1/2pisqrt(LC(
---
> 1/2pisqrt(LC)
Вывод можно использовать как инструкцию для преобразования одного файла в другой.
$ diff data/right.bin data/left.bin > data/patch.bin
$ patch data/left.bin data/patch.bin
patching file data/left.bin
$ cat data/left.bin
1/2pisqrt(LC)
Команда cat выводит всё содержание файла в терминал. Если файл большой, то буфер просмотра переполнится и найти нужный участок не получится. Постранично изучить файл помогут more, less или most. Чаще всего используют less.
$ less data/quote.mrs
... .... .- .-.. .-.. / .. / .-. . ..-. ..- ... . / -- -.-- / -.. .. -. -. . .-. / -... . -.-. .- ..- ... . / .. / -.. --- / -. --- - / ..-. ..- .-.. .-.. -.-- / ..- -. -.. . .-. ... - .- -. -.. / - .... . / .--. .-. --- -.-. . ... ... / --- ..-. / -.. .. --. . ... - .. --- -. ..--..
highway/data/quote.mrs (END)
Перемещаться по файлу можно стрелками вверх/вниз; для выхода нажмите q. Поиск — клавишей /. less также умеет работать с несколькими файлами: :e — открыть по имени, :p — предыдущий файл, :n — следующий.
Утилиту less позволяет следить за изменением файла в реальном времени — это полезно при работе с логами работающего процесса:
$ less +F README.patch
Для выхода нажмите Ctrl+C, затем Q. Команда tail работает аналогичным образом:
$ tail -f README.patch
Для объединения файлов используйте join. Команда сопоставляет строки по ключевому столбцу и объединяет соответствующие данные. По умолчанию ключ — первая колонка; при необходимости задайте его явно: -1 N для первого файла и -2 M для второго.
$ join data/dudes.tsv data/years.tsv
1 Galois 1811
2 Abel 1802
3 Uhlenbeck 1942
4 Srinivasa 1887
5 Stefan 1892
Команда пропустит те поля, для которых не нашлось совпадений (в нашем случае 1777 во втором файле). Чтобы их включить насильно используйте ключ -a n, где n — источник. Чтобы распечатать только те поля для которых не нашлось совпадений служит ключ -v.
xarg превращает вывод одной программы в аргументы другой, например:
$ cat download_list.txt | xargs wget –c
Допустим мы хотим создать архив только JPG-файлов, игнориуя RAW.
$ cd trip_pictures
$ find / -name *.jpg -type f -print | xargs tar -cvzf
trip_pictures.tar.gz
tr транслирует или удаляет символы в потоке. Можно перевести весь текст из нижнего регистра в верхний:
$ tr a-z A-Z < chsm/README.md
# CHSM — CHECKSUM
# ...
Удалим цифры из строки:
$ tr -d '0-9' <<< "S3XY"
SXY
Удалим всё кроме цифр:
$ tr -cd '0-9' <<< "S3XY"
3
Удалим повторяющиеся символы:
$ tr -s ' ' <<< "Hey, what's up?"
Hey, what's up?
Избавимся от всех непечатных символы:
$ echo -e "Test\n\t2\xAA\n" | tr -cd "[:print:]"
Test2
Команда поддерживает ряд готовых наборов,[:print:]один из них. Например,[:alnum:]— буквы и цифры,[:alpha:]— только буквы,[:digits:]— только цифры и т.д.
comm используя ввод источников как множества позволяет производить над ними операции.
-1— элементы набора A, которых нет в наборе B;-2— элементы набора B, которых нет в наборе A;-3— общие элементы наборов A и B.
$ comm -2 A B # shows only unique elements of A
$ comm -1 A B # shows only unique elements of B
$ comm -23 A B # substruct B from A
Одно из «супероружий» командной строки — утилита grep (Global Regular Expression Print). Она ищет совпадения по шаблонам в файлах и потоках. Утилита поддерживает множество опций; ниже — основные:
# Search for the word error without taking into account case (i.e. ERROR will also be found), -i
$ grep -i "error" data/log.txt
# Search all files (-r), including nested directories for error or warning mentions
#. (-E — regex) with line number (-n)
$ grep -rinE "INFO|WARN" data
# Set the requirement for a complete word match
# (post_transition_metal will be ignored)
$ grep -w "transition_metal" data/elements.tsv
# Show context (lines before/after):
# -A after, -B before, -C on both sides
$ grep -C 2 "Li" data/elements.tsv
Утилита поддерживает три синтаксиса регулярных выражений: базовый POSIX (BRE), расширенный POSIX (ERE, ключ -E) и Perl-совместимый (PCRE, ключ -P). Регулярные выражения полезны для извлечения, очистки и трансформации текста, а также для разбора вывода программ и веб-страниц. В примерах далее используем расширенный синтаксис POSIX (ERE, -E).
Регулярные выражения строятся из трёх компонентов: литералы, классы символов и квантификаторы (модификаторы). Частые метасимволы: * . ^ $ + ? ( ) [ ] { } | \.
[abc],[a-z]— совпадение одного символа из набора или диапазона;[^abc],[^a-z]— любой символ, кроме указанных;.— любой символ, кроме перевода строки\n;\— экранирование метасимволов.- Классы символов POSIX (те же, что и в
tr) поддерживаются и вgrep:
$ grep -E -o [[:digit:]] data/models.txt
3
Если важна позиция, используйте якоря начала/конца строки ^ и $:
$ grep -E 'ed$' data/log.txt
2025-02-02T12:00:01Z INFO Maxwell daemon has started
Квантификаторы уточняют количество совпадений: * — 0 или больше, т.е. любое, + — 1 или больше, ? — 0 или 1. Диапазоны: {m,n}. Если одна граница опущена, берётся «до бесконечности»: {2,} — два и более (по умолчанию — жадное совпадение).
Пример для телефонного номера вида +1-(XXX)-XXX-XXXX, где префикс страны опционален:
$ egrep "\([[:digit:]]{3}\)-[[:digit:]]{3}-[[:digit:]]{4}" highway/data/phones.txt
+7-(314)-159-2653
+381-(271)-828-1828
+1-(161)-803-3988
(555)-666-7777
egrepэквивалентенgrep -E.
Альтернация (логическое «или») задаётся |. Например, найти this/This можно так:
$ grep -E '(Th|th)is' file.txt
или проще — регистронезависимо:
$ grep -i 'this' file.txt
Возможности регулярных выражений куда шире краткого обзора — см. man 7 regex.
Есть ещё две мощных утилиты, sed и awk, но мы рассмотрим их отдельно.
Steam EDitor
Пора поговорить ещё об одном «секретном оружии» — sed (stream editor). Утилита читает данные из файла или stdin, применяет к каждой строке правила и пишет результат в stdout.
Если не указать команд, sed просто выведет содержимое файла:
$ sed '' file.txt
Команды пишутся в кавычках. Например, p — «распечатать»:
$ sed 'p' file.txt
Здесь каждая строка напечатается дважды: sed по умолчанию выводит обработанную строку, а команда p печатает её ещё раз. Это важный вывод: sed строчно-ориентирован. Чтобы подавить автоматический вывод, используйте флаг -n; добавив номер перед p, можно распечатать только нужную строку:
$ sed -n '1p' file.txt
apple 5 red
Шаговая адресация N~M позволяет выбирать периодические строки. Например, удалить каждую третью:
$ sed '1~3d'
Самый типичный сценарий — замена:
$ echo "A,D,C" | sed "s/D/B/"
A,B,C
Разделитель / можно задать другим, часто используют | или #:
$ pwd | sed "s.home.dome."
/dome/oliver
Чтение из одного файла и запись в другой:
sed "s/pizza/taquitos/" <italian_food.txt >mexican_food.txt
Править файл «на месте» — флаг -i. Чтобы сохранить бэкап, укажите суффикс:
$ sed -i "s/kek/lol/" phrase.txt
$ cat phrase.txt
You are funny, lol
Вы можете параллельно создать резервную копию, добавив после -i например .backup:
$ sed -i'.backup' 's/kek/lol/' phrase.txt
$ ls
phrase.txt phrase.txt.backup
По умолчанию заменяется первое совпадение в строке:
$ echo "one, one, one" | sed "s/one/two/"
two, one, one
Заменить все — добавьте g:
$ echo "one, one, one" | sed "s/one/two/g"
two, two, two
Только второе совпадение:
$ echo "one, one, one" | sed "s/one/two/2"
one, two, one
Обёртка чисел в скобки (используем ERE/-E):
$ echo "length 555 nm" | sed -r 's/[0-9]+/(&)/'
length (555) nm
& — это «всё, что совпало».
Удаление хвостовых пробелов во всех строках:
sed -i 's/ *//' file.txt
Пробел можно заменить на [[:space:]] чтобы удалить не только пробелы, но и табуляцию.
Когда мы обсуждали команду grep мы уже говорили, что можно использовать символы ^ и $ для обращения к началу и концу последовательности символов. Так как пустая строка это отсутствие символов, то удалить пустые строки можно так:
$ sed -i 's/^$//d'
Заменить все символы на высокий регистр (\U) или нижний (\L) можно так:
$ sed 's/[a-z]/\U&/g' file.txt
Команду для sed можно вынести в отдельный файл и переиспользовать в качестве аргумента:
$ echo "one, one, one" | sed -f script.sed
Вы так же можете встраивать переменные внутрь запроса:
$ echo "one, one, one" | sed -e "s/one/$USER/2"
one, oliver, one
sed крайне мощен для постобработки текста; в книге мы используем только базовые приёмы — остальное смотрите в man sed.
## AWK
Ещё одна сверхполезная утилита в Linux — awk (Aho, Weinberger and Kernighan). Это не просто утилита, а интерпретатор и язык программирования для обработки текста. Он позволяет писать короткие, но эффективные программы. Базовый синтаксис:
$ awk 'pattern { action }' file
awk часто применяют к тексту, разбитому на столбцы. Например, вывести второй столбец:
$ awk '{ print $2 }' data/dudes.tsv
Вывести строки, где значение во втором столбце больше 1777:
$ awk '$2 > 1777 { print $0 }' data/years.tsv
В $0 хранится вся строка.
Изменять вывод по условию можно через if:
$ awk '{ if ($1 < 50) print $2, "MegaMind"; else print $2, "Smart" }' data/dudes.tsv
Пронумеровать строки:
$ awk '{ print NR, $0 }' data/phones.txt
Посчитать сумму по столбцу:
$ awk '{ sum += $2 } END { print "Total:", sum }' data/elements.tsv
Многие программы печатают табличный вывод, например ls:
$ ls -l | awk '{ print $9, $5 }'
Полезные встроенные функции: length($0) — длина строки; tolower(), toupper() — регистр; substr(string, start, length) — подстрока; split(string, array, delimiter) — разбиение на массив. Важные переменные: NR — номер текущей записи (строки); NF — число полей в текущей записи; FS — входной разделитель (по умолчанию пробел); OFS — выходной разделитель полей (по умолчанию пробел); RS — разделитель записей (по умолчанию \n); ORS — выходной разделитель.
Искать строки по шаблону, как в grep, можно так:
$ awk '/ERROR/ { print $0 }' data/log.txt
Для описания awk потребуется много времени — наша же задача здесь — упомянуть о его существовании.
Работа с процессами
Операционная система Linux взаимодействует с процессами через сигналы — события, которые приходят асинхронно относительно выполнения кода. Сигнал может поступить в любой момент: выполнение прерывается, и управление передаётся обработчику (если он задан).
SIGHUP(1) — приходит при разрыве соединения с управляющим терминалом (часто используется демонами как команда перечитать конфигурацию).SIGINT(2) — прерывание от пользователя, обычно поCtrl+C.SIGKILL(9) иSIGTERM(15) — «мягкое» и «жёсткое» завершение процесса соответственно;SIGKILLнельзя перехватить или проигнорировать.SIGTSTP(20) — приостановка процесса поCtrl+Z.
Программы обрабатывают сигналы через… обработчики. Если обработчик не установлен, действует поведение по умолчанию (завершить, проигнорировать, приостановить или продолжить).
#include <signal.h>
#include <stdio.h>
void handle_signal(int signal) {
printf("Caught a signal %d\n", signal);
}
int main() {
signal(SIGINT, handle_signal); // Catch Ctrl+C
while (1) { }
return 0;
}
Какие-то сигналы можно перехватить, какие-то — нет (например, SIGKILL). В bash есть механизм перехвата через trap, но к нему вернёмся позже.
Большинство команд, которые мы запускали, завершались быстро. Но некоторые задачи работают долго и «занимают» терминал.
Запустим HTTP-сервер на Python — он заблокирует ввод. Нажмите Ctrl+Z, чтобы приостановить процесс:
$ python3 -m http.server 8000 --bind 0.0.0.0
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
^Z
[1] + 67851 suspended python3 -m http.server 8000 --bind 0.0.0.0
Число в квадратных скобках — номер задания (job) в текущей сессии, 67851 — PID процесса. Вернуть процесс на передний план можно командой fg, указав его номер:
$ fg %1
[1] + 67851 continued python3 -m http.server 8000 --bind 0.0.0.0
Если номер не указывать, fg возьмёт последнее задание из очереди.
Процесс возобновился и снова блокирует ввод. Ещё раз нажмите Ctrl+Z, затем отправьте его в фон командой bg:
$ bg %1
Другой способ запустить процесс в фоне — поставить & в конце команды. Например, sleep «ничего не делает» в течение указанного времени:
$ sleep 100500 &
[2] 68375
Терминал свободен, задание выполняется в фоне. Текущие задания показывает jobs:
$ jobs
[1] - running python3 -m http.server 8000 --bind 0.0.0.0
[2] + running sleep 100500
Чтобы завершить процесс, используйте kill. Завершим работу HTTP-сервера:
$ kill %1
[1] - 67851 terminated python3 -m http.server 8000 --bind 0.0.0.0
По умолчанию kill отправляет SIGTERM — «мягкий» запрос на завершение: программа может перехватить его и освободить ресурсы. Для безусловного завершения есть SIGKILL (например, kill -9 <PID>); этот сигнал перехватить нельзя. kill принимает как PID, так и спецификатор задания вида %N.
Выполните kill -l, чтобы посмотреть, какие сигналы поддерживает утилита.
Одна программа всё ещё работает в фоне — давайте её завершим. Несмотря на то, что нам уже известны и PID, и номер задания в сессии, найдём её другим способом.
В Linux есть утилита ps, которая показывает список текущих процессов; по умолчанию — только процессы, связанные с текущим терминалом (TTY).
$ ps
PID TTY TIME CMD
47695 pts/0 00:00:00 zsh
68375 pts/0 00:00:00 sleep
68670 pts/0 00:00:00 ps
Для вывода полной сводки по процессам всех пользователей часто используют ключи aux:
$ ps aux
Список получится длинным, искать в нём неудобно. Вместо связки ps | grep можно сразу воспользоваться pgrep:
$ pgrep -a sleep
68375
Найденный PID совпадает с тем, что мы видели ранее. Завершим sleep безусловно — сигналом SIGKILL:
$ kill -SIGKILL 68375
[2] + 68375 killed sleep 100500
Чаще пишут короче: kill -9 <PID>. Цифра 9 — это SIGKILL.
Как вы, наверное, уже поняли, процессы, запущенные из терминала, привязаны к его сессии. Если закрыть терминал, они получат SIGHUP и завершатся (некоторые могут перехватить или проигнорировать этот сигнал).
Сильная сторона мультиплексораtmuxв том, что у него есть собственный сервер. Всё, что вы делаете внутри сессииtmux, продолжит работать и после закрытия терминала. Можно отключиться локально, а потом подключиться удалённо и продолжить с места остановки.
Если нужно, чтобы процесс не завершался при SIGHUP, используйте nohup:
$ nohup python3 -m http.server 8000 --bind 0.0.0.0 &
[1] 69682
nohup: ignoring input and appending output to 'nohup.out'
По умолчанию nohup пишет вывод в nohup.out. Это можно подавить:
nohup python3 -m http.server 8000 --bind 0.0.0.0 > /dev/null 2>&1 &
Что это и как это работает — рассмотрим позже.
nohup перехватывает SIGHUP и игнорирует его, поэтому терминал можно закрыть — процесс продолжит работу в фоне. Однако задание всё ещё числится за текущей сессией. Чтобы полностью отвязать его от терминала, выполните встроенную команду оболочки disown. Аналогично можно «отцепить» любой фоновый процесс, даже запущенный без nohup.
В Linux у каждого процесса есть значение nice: диапазон от −20 (наивысший приоритет) до 19 (наименьший). Чем ниже nice, тем выше приоритет и тем больше времени процессора получит процесс. По умолчанию — 0.
Запустить команду с заданным nice можно так, проверить — через ps:
$ nice 15 sleep 100500 &
74584
$ ps -o pid,comm,nice
PID COMMAND NI
71910 zsh 0
74584 sleep 15
74613 ps 0
Повышать приоритет (т. е. задавать отрицательный nice) может только администратор. Изменить nice у уже запущенного процесса — renice:
$ sudo renice -10 -p 74584
[sudo] password for oliver:
74584 (process ID) old priority 15, new priority -10
➜ ~ ps -o pid,comm,nice
PID COMMAND NI
71910 zsh 0
74584 sleep -10
74689 ps 0
Сборка исходного кода хорошо параллелится; make поддерживает флаг -j. Обычно не стоит превышать число логических ядер. Узнать его можно через nproc:
$ make -j $(nproc)
Когда запущено несколько фоновых задач, а вам нужно дождаться всех, используйте wait:
$ sleep 7 &; PID_1=$!; sleep 5 &; PID_2=$!; wait; \
echo "Processes $PID_1 & $PID_2 are finished!"
[1] 79242
[2] 79243
[2] + 79243 done sleep 5
[1] + 79242 done sleep 7
Processes 79242 & 79243 are finished
Иногда полезно наблюдать за командой, периодически перезапуская её. Для этого есть watch:
$ watch -n 5 date
В примере date будет запускаться каждые 5 секунд, обновляя значение на экране.
Можно и просто измерить время выполнения произвольной команды:
start=$(date +%s)
# Your command here
sleep 2
end=$(date +%s)
echo "Execution time: $((end - start)) seconds"
Прочие полезные команды
Есть полезная, но опасная команда — eval. Она интерпретирует входную строку как команду и выполняет её:
$ cmd="echo hey!"
$ eval "$cmd"
hey!
Используйте eval только на доверенном вводе: подстановка произвольной строки может привести к выполнению вредоносного кода.
Пример кодирования и декодирования в BASE64:
$ echo -n "Euler" | base64
RXVsZXI=
$ echo -n "RXVsZXI=" | base64 -d
Euler
od (octal dump) выводит данные в удобном двоичном представлении. Например, шестнадцатерично по байтам:
$ echo "highway" | od -t x1
0000000 68 69 74 68 77 61 79 0a
Опции из семейства -t: x1 — hex по 1 байту, x2 — по 2 байта, есть и десятичные/восьмеричные форматы.
date форматирует и вычисляет даты:
$ date +"%A,%B %d %Y"
Sunday,February 02 2025
Разница в днях между двумя датами (надёжнее считать по секундам эпохи):
$ start="2025-02-02"
$ end="2025-05-03"
days=$(( ($(date -d "$end" +%j) - $(date -d "$start" +%j)) ))
echo "Difference: $days days"
Difference: 90 days
Посмотреть дату «через 90 дней» (например, дедлайн по шенгену):
$ date -d "+90 days" +"%Y-%m-%d"
2025-05-03
$ date -d "2025-02-02 +90 days" +"%Y-%m-%d"
2025-05-03
В Linux есть несколько утилит для работы с архивами. Часто сжатие происходит в два этапа, сначала файлы объединяются в так называемый tarball с помощью утилиты tar, а уже затем сжимаются утилитами gzip (.tar.gz, tgz), bzip2 (.tar.bz2, .tbz2) или xz (.tar.xz or .txz). Утилиты используют разные алгоритмы сжатия. Также есть утилиты zip/unzip и 7z.
# -c — new archive, -v — show progress, -f — specify filename
$ tar -cvf archive.tar file1 file2 directory/
# -z — gzip, -j — bzip2, -J — xz
$ tar -cJvf archive.tar.gz file1 file2 directory/
# -x — extract
$ tar -xzvf archive.tar.gz
Сжатие/распаковка одиночных файлов:
# Compress
$ gzip data/commands.txt
# Uncompress
$ gunzip commands.txt.gz
Для просмотра содержимого файла без его распаковки можно использовать утилиты zcat, bzcat, xzcat. Для zip архивов используется unzip -p.
$ zcat data/commands.txt.gz
date
uptime
Сравнить два бинарных файла:
diff -y <(xxd data/left.bin) <(xxd data/right.bin)
Работа с сетью
В Linux много сетевых утилит. Всё охватить не получится, поэтому — базовые инструменты и несколько полезных команд.
Первая, о которой стоит знать, — ip. Она пришла на смену ifconfig, route и arp и даёт единый интерфейс для настройки интерфейсов, адресов, маршрутов и соседей.
$ ip [option] [object] [command]
Объекты: link, addr/address, route, neigh, tunnel и т. д. Команды: add, del/delete, show/list и др.
Информация об активных интерфейсах:
$ ip address show
Есть и более короткая форма — ip a. Поднять и отключить интерфейс можно через link:
$ ip link set eth0 up
$ ip link set eth0 down
Если возникла сетевая проблема, ip — первая точка входа.
Чтобы узнать IP-адреса устройств в локальной сети, начните с ARP-таблицы:
$ ip neigh show
Если взаимодействия ещё не было, записей не окажется. Тогда помогут сканеры. Для начала посмотрите свою подсеть:
$ ip route show
192.168.1.0/24 dev wlp0s20f3 proto kernel scope link src 192.168.1.105 metric 600
Простейший обход адресов (наглядно, но неэффективно):
$ for i in {1..254}; do ping -c 1 192.168.1.$i | grep "bytes from"; done
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=3.03 ms
64 bytes from 192.168.1.12: icmp_seq=1 ttl=64 time=302 ms
# ...
ping шлёт ICMP-пакеты одному адресу за раз; для диапазонов удобнее fping.
Так или иначе, ping не предназначен для сканирования; nmap — сетевой сканер:
$ nmap -sn 192.168.1.0/24
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-05 16:26 UTC
Nmap scan report for DeviceDHCP.Home (192.168.1.1)
Host is up (0.0039s latency).
# ...
-sn де-факто делает «ping-скан» подсети. nmap умеет и больше, например предположить ОС удалённого хоста:
$ sudo nmap -O 192.168.1.1
# ...
Running (JUST GUESSING): Microsoft Windows 11|10|2022|Phone|2008 (97%)
$ sudo nmap -O 192.168.1.106
# ...
Running: Linux 3.X|4.X
Проверить открытые порты:
$ sudo nmap -sS 192.168.1.1
# ...
PORT STATE SERVICE
80/tcp open http
443/tcp open https
631/tcp open ipp
8080/tcp open http-proxy
9100/tcp open jetdirect
Также можно проверять только отдельные порты или их диапазоны (-p).
Для локальной статистики по сокетам вместоnetstatудобнее современная утилитаss.
Утилиты dig, nslookup, whois и host помогут отправлять DNS‑запросы. Можно вычислить IP‑адрес или узнать MX или A записи.
$ host -t a google.com
google.com has address 142.250.72.14
В некоторых случаях при отладке каких‑нибудь действий, связанных с сетью, бывает полезно записать трафик и «проиграть» его снова. Поможет сделать это утилита — tcpdump.
# Log all (TCP and UDP) packets
$ sudo tcpdump -i <interface> -w output.pcap 'tcp or udp'
# the same, but only from a specific IP
$ sudo tcpdump -i eth0 -w output.pcap 'host <source-ip> and (tcp or udp)'
# or from a specific port
$ sudo tcpdump -i eth0 -w output.pcap 'port 12345 and (tcp or udp)'
Просмотреть трафик можно через эту же утилиту:
$ tcpdump -r output.pcap
Чтобы проиграть весь получившийся трафик часто используют другую утилиту — netcat. Она же ncat или nc. Она предназначена для передачи данных по сети с одной машины на другую:
# Let's record the traffic
$ sudo tcpdump -i eth0 -A 'tcp or udp' > payloads.txt
# Let's play TCP traffic
$ nc <target-ip> <target-port> < payload.txt
# Let's play UDP traffic
$ nc -u <target-ip> <target-port> < payload.txt
nc умеет и простое сканирование портов:
$ nc -zv google.com 443
Connection to google.com (142.250.72.14) 443 port [tcp/https] succeeded!
С его же помощью можно поднять простой TCP‑ или UDP‑сервер, что весьма полезно при отладке приложений.
# TCP server
$ nc -l 80
GET / HTTP/1.1
Host: 192.168.1.105
Connection: keep-alive
Вы можете передать файл с другого компьютера на том, что поднят сервер:
# server
$ nc -l 12345 > file.txt
# client
$ cat file.txt | nc <receiver_ip> 12345
Иногда в целях отладки нужно вводить задержки в передаче данных в сети, чтобы проверить работу тайм‑аутов соединений или работу каких‑нибудь буферов. Стандартными утилитами такое не сделать, но вы можете воспользоваться такими утилитами, как, например, speedbump.
Другая очень важная утилита — ssh. Она позволяет заходить в консоль удалённой машины.
nc однако не лучший выбор для передачи файлов. Как минимум вам нужно поднять сервер на стороне удалённой машины. Чаще всего используют ssh. На стороне сервера поднимается сервис, который позволяет производить подключения по шифрованному каналу. Скопировать файлы через ssh позволяет утилита scp:
$ scp ./file.txt user@server.ip:~/
Если вы не хотите менять имя файла, то его можно опустить во второй части. Подключиться к удалённому терминалу можно через утилиту ssh:
$ ssh user@server.ip
По умолчанию ssh использует 22 порт, но его часто меняют на другой из соображений безопасности. Если это так, то используйте ключик -p (-P для scp) и укажите нужный порт.
Утилита ssh также позволяет запускать графические приложения на удалённой машине и отображать на вашей (прекрасно работает на X11, но есть нюансы с Wayland):
$ ssh -X user@remote_host
(remote) $ code .
В зависимости от приложения и качества связи приложение будет работать либо очень хорошо, либо с большими зависаниями.
Ещё одна полезная функция ssh — это возможность поднятия туннеля. Допустим, у вас есть удалённый сервер и вы хотите открывать веб-страницы через него. Во‑первых, поднимите SOCKS‑туннель:
$ ssh -ND 1867 user@example.com -p 12345
И настройте proxy в своём браузере.
Через ssh также работает утилита sftp, которая предоставляет более удобный интерфейс для обмена файлами, нежели scp. Вы можете подключиться на удалённую машину, погулять по директориям и уже со стороны удалённой машины отправить нужные файлы.
Для скачивания чего‑либо в интернете удобно использовать утилиту wget. Она может делать это рекурсивно (т. е. выкачать весь сайт), и ей также можно задать ограничение скорости:
$ wget --limit-rate=200k https://example.com/distro.iso
Существенный недостаток утилиты — она не может работать с потоками, что очень важно в Linux. Вместо неё часто используют утилиту curl:
$ curl https://example.com/distro.iso > distro.iso
По умолчанию она выводит всё содержимое в стандартный поток вывода, поэтому в примере выше поток перенаправлен в файл.
Утилита curl позволяет в том числе отправлять HTTP‑запросы с более удобным интерфейсом, нежели команда nc, где его вам пришлось бы прописывать вручную. Поднимем HTTP‑сервер:
$ python3 -m http.server 8000 --bind 0.0.0.0
И отправим POST запрос:
$ curl -X POST 0.0.0.0:8000
