ServerAdmin.ru
28.9K subscribers
305 photos
35 videos
13 files
2.63K links
Авторская информация о системном администрировании.

Информация о рекламе: @srv_admin_reklama_bot
Автор: @zeroxzed

Второй канал: @srv_admin_live
Сайт: serveradmin.ru
Download Telegram
​​У меня в управлении много различных серверов. Я обычно не заморачивался с типом файловых систем. Выбирал то, что сервер ставит по умолчанию. Для серверов общего назначения особо нет разницы, будет это XFS или EXT4. А выбор обычно из них стоит. RPM дистрибутивы используют по умолчанию XFS, а DEB — EXT4.

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

1️⃣ XFS можно расширить, но нельзя уменьшить. EXT4 уменьшать можно. На практике это очень редко надо, но разница налицо.

2️⃣ У EXT4 по умолчанию создаётся не очень много inodes. Я нередко упирался в стандартное ограничение. В XFS их по умолчанию очень много, так как используется динамическое выделение. С проблемой нехватки не сталкивался ни разу.

3️⃣ EXT4 по умолчанию резервирует 5% свободного места на диске. Это можно изменить при желании. XFS если что-то и резервирует, то в разы меньше и это не настраивается.

❗️У меня была заметка про отличия ext4 и xfs. Можете почитать, кому интересно. Рассказать я хотел не об этом. Нередко нужно узнать, какая файловая система используется, особенно, когда закончилось свободное место. Для этого использую команду mount без ключей:
# mount

Она вываливает трудночитаемую лапшу в терминал, где трудно быстро найти корневой или какой-то другой раздел. Конкретный раздел ещё можно грепнуть, а вот корень никак. Я всё думал, как же сделать, чтобы было удобно. Просмотрел все ключи mount или возможности обработки вывода. Оказалось, нужно было подойти с другой стороны. У утилиты df есть нужный ключ:
# df -T
Filesystem   Type    1K-blocks  Used Available Use% Mounted on
udev       devtmpfs  1988408    0   1988408   0% /dev
tmpfs      tmpfs     401244    392   400852   1% /run
/dev/sda2    ext4    19948144 2548048  16361456  14% /
tmpfs      tmpfs    2006220    0   2006220   0% /dev/shm
tmpfs      tmpfs     5120     0    5120   0% /run/lock
/dev/sda1    vfat     523244   5928   517316   2% /boot/efi
tmpfs      tmpfs     401244     0   401244   0% /run/user/0

И не надо мучать mount. У df вывод отформатирован, сразу всё видно.

#bash #terminal
​​В комментариях к заметке, где я рассказывал про неудобочитаемый вывод mount один человек посоветовал утилиту column, про которую я раньше вообще не слышал и не видел, чтобы ей пользовались. Не зря говорят: "Век живи, век учись". Ведение канала и сайта очень развивает. Иногда, когда пишу новый текст по трудной теме, чувствую, как шестерёнки в голове скрипят и приходится напрягаться. Это реально развивает мозг и поддерживает его в тонусе.

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

Структурируем вывод mount:
# mount | column -t
Получается очень аккуратно и читаемо. Ничего придумывать не надо, чтобы преобразовать вывод.

А вот пример column, но с заменой разделителя на двоеточие:
# column -s ":" -t /etc/passwd
Получается удобочитаемое представление. Из него можно без особых проблем вывести любой столбец через awk. Как по мне, так это самый простой способ, который сразу приходит в голову и не надо думать, как тут лучше выделить какую-то фразу. Выводим только имена пользователей:
# column -s ":" -t /etc/passwd | awk '{print $1}'
Каждый пользователь в отдельной строке. Удобно сформировать массив и передать куда-то на обработку.

Утилита полезная и удобная. Главное теперь про неё не забыть, чтобы применить в нужный момент.

#bash #terminal
​​Во времена развития искусственного интеллекта сидеть и вспоминать команды и ключи консольных утилит Linux уже как-то не солидно. Пусть "вкалывают роботы, а не человек".

Идём на сайт https://www.askcommand.cppexpert.online и приказываем железным мозгам выполнять наши команды:

I want to delete 10 first lines in text file.
sed '1,10d' filename

Извольте проверить. Команда работает. Правда, реально не удаляет строки, но выводит содержимое файла без первых десяти строк. Попробуем что-то посложнее:

I want to find all php files and replace one word there with another.
find ./ -name "*.php" -exec sed -i 's/old/new/g' {} +

Опять сработало. Рабочий вариант предложил. Попробуем ещё усложнить:

I want to find the ten largest files older than thirty days and move them to another directory.
find /source/directory -type f -mtime +30 -exec ls -s {} \; \
| sort -n -r | head -10 | awk '{print $2}' \
| xargs -I '{}' mv '{}' /destination/directory

Я не проверял этот вариант, но навскидку рабочий. Не вижу тут ошибок. Если английский не знаем, то напрягаем другого робота: https://translate.yandex.ru.

Немного запереживал о будущем, которое нас ждёт. На кого переучиваться предстоит? Таксисты и водители автобусов тоже будут не нужны. С программистами и так всё понятно. Тестировал плагин для IDE, который приказывает кремниевым мозгам писать код на JavaScript и, знаете, у него получается 😲.

#сервис #bash
​​Смотрите, какая прикольна штука есть для подключения по SSH из консоли - sshto. Это небольшой bash скрипт, который позволяет через псевдографическое меню управлять преднастроенными SSH подключениями. Хорошее решение для самодельного jumphost.

Это когда вы используете промежуточный сервер для подключения к целевым серверам. Такой подход позволяет гибко управлять доступом на основе пользователей jump севера, логировать команды и записывать вывод консоли. И всё без каких-то специализированных решений. В основном средствами самого Linux и его небольших утилит. У меня в разное время были различные заметки по этой теме. Если интересно, могу собрать их в одну.

Вернёмся к sshto. Как я уже сказал, это bash скрипт, который читает конфигурацию из файла ~/.ssh/config и выводит список серверов оттуда в псевдографическое меню. Вот пример такого файла:

#Host DUMMY #Moscow#

Host server-number-one #First Server
HostName 1.2.3.4
port 22777
user root

Host server-number-two #Second server
HostName 4.3.2.1
port 22888
user username

#Host DUMMY #Saint Petersburg#

Host server-number-three #Third server
HostName 5.6.7.8
port 22
user user01

Host server-number-four #Fourth server
HostName 9.8.7.6
port 22
user user02

Ставим sshto:
# git clone https://github.com/vaniacer/sshto
# cd sshto/
# cp sshto /usr/bin/
Запускаем:
# sshto

Видим меню, такое же как, в приложенной картинке. Помимо непосредственно подключений по SSH, скрипт умеет там же, на удалённых серверах, сразу же выполнять некоторые команды.

#ssh #bash #script
​​В Linux есть подсистема ядра, которая позволяет получать уведомления о событиях, связанных с файлами и каталогами файловой системы. Называется Inotify. Я упоминал о ней недавно, когда рассказывал про Lsyncd, которая получает информацию от inotify и запускает синхронизацию при изменениях в файлах.

На основе inotify можно организовать простой мониторинг за изменениями файлов. Реализовать это можно, к примеру, с помощью утилиты fswatch, которая есть в базовых репозиториях практически всех популярных систем (GNU/Linux, *BSD, Mac OS X, Solaris). В каждой системе она выбирает свой механизм ядра для уведомлений. В Linux это inotify.

Устанавливаем fswatch:
# apt install fswatch

Для наблюдения за файлом или каталогом достаточно просто запустить fswatch в консоли:
# fswatch /tmp
Всё, что будет происходить с файлами и директориями в /tmp, будет выводиться в консоль. Но в таком виде утилита малоинформативна и на практике бесполезна. Запускать лучше сразу с парочкой дополнительных ключей:
-x - отображать тип событий
-t - отображать временные метки
# fswatch -x -t /tmp

Откроем рядом ещё одну консоль, создадим, обновим и удалим в директории /tmp файл:
# echo '123' > file.txt
# rm file.txt
В логе при этом будет следующее:
Thu 16 Nov 2023 09:14:10 PM MSK /tmp/file.txt Created
Thu 16 Nov 2023 09:14:10 PM MSK /tmp/file.txt PlatformSpecific
Thu 16 Nov 2023 09:14:10 PM MSK /tmp/file.txt Updated
Thu 16 Nov 2023 09:14:25 PM MSK /tmp/file.txt Removed

Это уже более прикладное решение. Можно запустить fswatch в фоне, вывод направить, например, в текстовый файл и анализировать его с помощью Zabbix, реагируя на какие-то события. В статье рассказано в том числе как настроить триггеры. Первое, что приходит в голову, это сделать вот так:
# fswatch -x -t /tmp >> /var/log/fswatch.log &
Утилита будет работать в фоне и писать в файл изменения. Можно добавить запуск через cron по событию @reboot, чтобы запускать при старте системы.

Надёжнее написать запуск через systemd, создав файл /etc/systemd/system/fswatch.service:

[Unit]
Description=Start fswatch file monitor
Wants=fswatch.service
[Service]
ExecStart=fswatch -x -t /tmp
[Install]
WantedBy=multi-user.target

Запускаем:
# systemctl start fswatch.service
Это самый простой вариант. Лог будет писаться сюда же, в journald. Смотреть можно так:
# journalctl -u fswatch.service
И дальше обрабатывать как обычные systemd логи. Можно вывести эти логи в отдельный namespace и отправить в централизованное хранилище.

Простой и эффективный способ решения задачи минимальными сторонними средствами. Можно обернуть в любую логику с помощью bash и использовать по потребностям. Например, на почту что-то слать при удалении файла, или сразу в Telegram. Тип событий указывается отдельным ключом:
# fswatch --event Removed -x -t /tmp

#linux #bash
Казалось бы, что может быть проще копирования файлов из одной директории в другую. Тем не менее в консоли Linux это не всегда простая и очевидная процедура. Для этого существует небольшая утилита cp, которая обычно присутствует во всех дистрибутивах. Лично у меня сложности возникают с тем, что я начинаю вспоминать, нужно ли использовать *, ставить или нет слеш на конце директории и т.д. Всё это влияет на конечный результат.

Например, надо скопировать содержимое одной директории в другую. Вроде бы ничего сложного:

# cp /dir_a/* /dir_b

Но так скопируются только файлы, без вложенных директорий. Добавляем ключ -r или сразу -a, чтобы и все атрибуты скопировать:

# cp -a /dir_a/* /dir_b

По моему субъективному мнению, логично было бы по умолчанию копировать рекурсивно и с сохранением всех атрибутов.

Но и в таком копировании есть различные неочевидные моменты. Использование звёздочки обрабатывает сама оболочка bash. Она передаёт команде cp аргументы, заменяя звёздочку. Если в директории не будет файлов, то cp вернёт ошибку, потому что оболочка ей ничего не передаст. А если файлов наоборот будет очень много, то может и зависнуть, потому что оболочка передаст cp примерно следующее:

# cp -a /dir_a/file1 /dir_a/file2 /dir_a/file3 ...... /dir_b

На длину такой команды есть ограничение оболочки. Не помню, как её посмотреть, но не суть. Важно понимать, что оно есть, и что * в командах, запускаемых в bash, работает именно так.

Ещё засаду с cp можно получить, если в директории будут файлы, начинающиеся с точки. Те же .htaccess. Если скопируем предложенной выше командой со *, то все файлы с точкой в начале имени потеряем. Будет неприятно. Это опять же особенность bash, которая по умолчанию трактует такие файлы как скрытые. Такое поведение можно изменить.

Скопировать файлы в консоли Linux можно большим количеством способов и инструментов. Но всегда, когда используете * (wildcard) в командах, имейте ввиду, что на самом деле это означает и к каким последствиям может привести. Если можно без них, то лучше обойтись.

Пример с тем же cp, только без * :

# cp -aT /dir_a /dir_b

Тут и файлы с точками не потеряли, и разворачивание звёздочки в длиннющую команду не происходит.

#bash
Вчера в заметке про копирование с помощью cp в комментариях справедливо привели ещё один вариант копирования. Причём самый простой и удобный. Я про него знал, но не стал упоминать, потому что нужны будут пояснения для тех, кто не знает про некоторые особенности псевдопапок в виде точки или двух точек. Речь идёт о примерно такой конструкции:

# cp -a /dir_a/. /dir_b

Мы всё содержимое директории dir_a со всеми скрытыми файлами скопировали в dir_b.

В Linux, как и в DOS кстати, существуют некие псевдопапки-ссылки на текущую и вышестоящую директории. Точка - это, соответственно, текущая директория, а две точки - вышестоящая по отношению к текущей. Обращаться к ним можно так же, как и к обычным папкам. Они, собственно, на вид ничем от остальных и не отличаются. Если сделать ls -la, то мы их увидим вместе с остальными директориями:
# ls -la
total 56
drwx------ 8 root root 4096 Nov 22 21:37 .
drwxr-xr-x 18 root root 4096 Oct 13 12:30 ..
-rw------- 1 root root 6115 Nov 22 16:02 .bash_history
-rw-r--r-- 1 root root 571 Apr 10 2021 .bashrc
drwx------ 3 root root 4096 Dec 9 2022 .cache
drwx------ 4 root root 4096 Nov 16 21:22 .config
-rw-r--r-- 1 root root  56 Nov 15 00:19 .gitconfig
drwx------ 3 root root 4096 Dec 9 2022 .local
-rw-r--r-- 1 root root 161 Jul 9 2019 .profile
-rw-r--r-- 1 root root  72 Dec 9 2022 .selected_editor
drwx------ 2 root root 4096 Nov 15 00:21 .ssh

Можно сделать:
# cd .
и остаться в текущем каталоге, либо:
# cd ..
и перейти в вышестоящий, родительский каталог для текущего.

Отсюда становится понятно, почему для запуска скрипта в текущем каталоге, мы используем точку:

# ./script.sh

Явно указываем на скрипт, который запускаем. Иначе оболочка проверит $PATH, там не окажется текущего каталога и скрипт не будет выполнен. Так что надо написать либо полный путь, либо использовать точку, если скрипт в текущем каталоге. Точно помню, что вот эту тему с точкой я очень долго не понимал и просто использовал. Даже мысли не было, что это я текущий каталог указываю. Думал, это какой-то признак исполняемого файла.

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

# touch ../file.name

Создали файл в вышестоящем каталоге, ничего не зная про него. Без двух точек нам сначала пришлось бы выяснить, где мы находимся, чтобы определить родительский каталог. А так всё делаем сразу.

Если я не ошибаюсь, то точка и две точки это просто ссылки на inode конкретных директорий. Так что их наличие будет зависеть от файловой системы. В наиболее популярных они присутствуют.

#bash
Хочу напомнить про 2 очень полезные утилиты Linux, с помощью которых удобно в скриптах делать проверки наличия подключенных и примонтированных блочных устройств и файловых систем. Речь пойдёт про findmnt и findfs.

Про первую я уже когда-то рассказывал. Findmnt удобна и полезна сама по себе, без привязки к скриптам. Просто запустите её и посмотрите вывод. Она выводит в консоль подробную информацию о всех точках монтирования. А ключ -x ещё и позволяет проверить отредактированный файл fstab на наличие в нём ошибок. Рекомендую запомнить эту возможность и использовать:

# findmnt -x
Success, no errors or warnings detected

А если в чём-то ошибётесь, то получите ошибку:

# findmnt -x
/mnt/backup
  [E] unreachable on boot required source: UUID=151ea24d-977a-412c-818f-0d374baa5012

Findfs сама по себе ничего не выводит. Она умеет искать файловые системы по заданными параметрами В качестве аргумента принимает значение LABEL, UUID, PARTLABEL и PARTUUID. Например так:

# findfs "UUID=151ea24d-977a-412c-818f-0d374baa5013"
/dev/sda2

Нашли файловую систему на /dev/sda2 с заданным UUID. При этом код выхода будет 0:

# echo $?
0

Если файловая система не будет найдена, код будет 1:

# findfs "UUID=151ea24d-977a-412c-818f-0d374baa5012"
findfs: unable to resolve 'UUID=151ea24d-977a-412c-818f-0d374baa5012'
# echo $?
1

Соответственно, подобную проверку мы может использовать в скриптах перед тем, как выполнять какие-то действия. Это актуально для каких-нибудь бэкапов или синхронизаций на сетевых или внешних дисках. Делаем простую проверку, типа такой:

if findfs "UUID=$1" >/dev/null; then 
echo "$1 connected."
else
echo "$1 not connected."
fi

Вместо echo можно сразу выполнять какое-то действие. Оно будет выполнено, если указанный скрипту UUID подключен. То есть сам скрипт работает так:

# ./check-fs.sh 151ea24d-977a-412c-818f-0d374baa5013
151ea24d-977a-412c-818f-0d374baa5013 connected.

И точно так же по аналогии можно сделать проверку точек монтирования с помощью findmnt:

if findmnt -rno TARGET "$1" >/dev/null; then 
echo "$1 mounted."
else
echo "$1 not mounted."
fi

Проверяем:

# ./check-mnt.sh /mnt/extbackup
/mnt/extbackup not mounted.

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

Я и сам с таким сталкивался, и много раз видел вопросы людей на тему того, что не могут понять, что происходит. Обычно это выглядит так. В момент бэкапа сетевой диск не был примонтирован и мы забили весь корень файлами. А потом в какой-то момент этот диск примонтировался и мы больше не видим те файлы, что были скопированы в точку монтирования в тот момент, когда там не было диска. Его надо отмонтировать и удалить локальные файлы. Если это сразу не просечёшь, то можно много времени потратить на поиск того, что занимает всё свободное место на разделе.

Так что назначайте метки внешним дискам, проверяйте их, делайте проверки монтирования сетевых дисков и т.д. Не выполняйте копирования и синхронизации без этих проверок. А то можно сильно удивиться из-за какой-нибудь неожиданной ошибки. Я реально сталкивался сам с этим не раз, пока не начал постоянно добавлять проверки. Вроде думаешь в локалке всё стабильно, сервера и сеть никто не дёргает, всё в одной стойке стоит. А потом оказалось, что после выключения электричества сервера поднялись не равномерно и сервер с бэкапами поднялся позже остальных. В итоге куда-то не примонтировался сетевой диск для бэкапов и они начали литься локально, пока там место не кончится.

#bash #script
​​Кажется, я ни разу не упоминал про простой и быстрый способ отладки bash скрипта. Для этого достаточно в самое начало написать:

set -x

В этом случай bash начнёт выводить в консоль каждую команду, которую он выполняет. Успешно выполненная команда помечается плюсом в начале строки. Не сказать, что это прям очень сильно помогает отладить, если есть ошибка, но в целом проще и удобнее. Особенно если скрипт большой, с условиями, вычислением переменных и т.д. Вы всё будете видеть на каждом этапе.

Покажу как это работает на простом скрипте по добавлению swap в систему:

#!/bin/bash

set -x

read -p 'Enter swap size in megabytes: ' size_mb
size_kb=$((1024*${size_mb}))
dd if=/dev/zero of=/swap bs=1024 count=${size_kb}
chmod 0600 /swap
mkswap /swap
swapon /swap
if [ "$(grep '/swap' /etc/fstab)" ]; then
  echo "Error: file /etc/fstab already has 'Swap' record"
else
  echo "Add Swap record to /etc/fstab"
  echo -e '\n/swap swap swap defaults 0 0' >> /etc/fstab
fi
swapon --show

Запускаем скрипт:

# ./script.sh 
+ read -p 'Enter swap size in megabytes: ' size_mb
Enter swap size in megabytes: 512
+ size_kb=524288
+ dd if=/dev/zero of=/swap bs=1024 count=524288
524288+0 records in
524288+0 records out
536870912 bytes (537 MB, 512 MiB) copied, 1.26859 s, 423 MB/s
+ chmod 0600 /swap
+ mkswap /swap
Setting up swapspace version 1, size = 512 MiB (536866816 bytes)
no label, UUID=e873faad-881e-4df0-b25f-23580b952738
+ swapon /swap
++ grep /swap /etc/fstab
+ '[' '' ']'
+ echo 'Add Swap record to /etc/fstab'
Add Swap record to /etc/fstab
+ echo -e '\n/swap swap swap defaults 0 0'
+ swapon --show
NAME TYPE SIZE USED PRIO
/swap file 512M  0B  -2

Благодаря set -x мы видим каждое действие, которое выполняет скрипт. Оно начинается с +, а дальше виден стандартный вывод после этого действия. Так разбирать работу скрипта проще, потому что в командах видны все переменные. В этом примере это не очень актуально, а когда они будут неявно указаны, а вычисляемые, то это может сильно помочь.

Когда не знал про set -x, выводил нужные мне переменные в разных местах скрипта с помощью echo во время отладки, чтобы понимать, что там происходит после каждой команды. Но этот путь намного проще.

#bash #script
​​Почти во всех популярных дистрибутивах Linux в составе присутствует утилита vmstat. С её помощью можно узнать подробную информацию по использованию оперативной памяти, cpu и дисках. Лично я её не люблю, потому что вывод неинформативен. Утилита больше для какой-то глубокой диагностики или мониторинга, нежели простого использования в консоли.

Если есть возможность установить дополнительный пакет, то я предпочту dstat. Там и вывод более наглядный, и ключей больше. А информацию по базовому отображению памяти хорошо перекрывает утилита free.

Тем не менее, если хочется быстро посмотреть некоторую системную информацию, то можно воспользоваться vmstat. У неё есть возможность выводить с определённым интервалом информацию в консоль. Иногда для быстрой отладки это может быть полезно. Запускать лучше сразу с парой дополнительных ключей для вывода информации в мегабайтах и с более широкой таблицей:

# vmstat 1 -w -S M

Как можно убедиться, вывод такой себе. Сокращения, как по мне, выбраны неудачно и неинтуитивно. В том же dstat такой проблемы нет. Но в целом привыкнуть можно. В man vmstat они подробно описаны, так что с интерпретацией проблем не должно возникать.

А вообще, эта заметка была написана, чтобы в неё тиснуть необычный однострочник для bash, который меня поразил своей сложностью и непонятностью, но при этом он рабочий. Увидел его в комментариях на хабре и сохранил. Он сравнивает вывод информации об использовании памяти утилиты free и cat /proc/meminfo:

# eval $(free -kw | awk '/Mem/{print "mtotal="$2"\nmused="$3"\nmfree="$4"\nmshared="$5"\nmbuffers="$6"\nmcache="$7"\nmavailable="$8}/Swap/{print "stotal="$2"\nsused="$3"\nsfree="$4}');eval $(awk -F'(: +)| ' '!/\(/{print $1"="$2}' /proc/meminfo);clear; echo -e ";;;;;\nMem;total;$mtotal;│;$MemTotal;MemTotal\nMem;used;$mused;│;$[MemTotal-MemFree-Buffers-Cached-KReclaimable];MemTotal-MemFree-Buffers-Cached-KReclaimable\nMem;free;$mfree;│;$MemFree;MemFree\nMem;shared;$mshared;│;$Shmem;Shmem\nMem;buffers;$mbuffers;│;$Buffers;Buffers\nMem;cache;$mcache;│;$[Cached+KReclaimable];Cached+KReclaimable\nMem;available;$mavailable;│;$MemAvailable;MemAvailable\nSwap;total;$stotal;│;$SwapTotal;SwapTotal\nSwap;used;$sused;│;$[SwapTotal-SwapFree];SwapTotal-SwapFree\nSwap;free;$sfree;│;$SwapFree;SwapFree\n\n" | column -t -s ";" --table-columns " ,free -kw¹,KiB¹, ,KiB²,/proc/meminfo²" --table-right "free -kw¹,KiB¹" --table-truncate "/proc/meminfo²"

Это прям какой-то царь-однострочник. Я когда его первый раз запускал, не верил, что он заработает. Но он заработал. В принципе, можно сохранить и использовать.

В завершении дам ссылку на свою заметку с небольшой инструкцией, как и чем быстро в консоли провести диагностику сервера, если он тормозит. Обратите внимание там в комментариях на вот этот. Его имеет смысл сохранить к себе.

#bash #script #perfomance
​​Недавно, когда вырезал из дампа mysql содержимое одной таблицы с помощью sed, подумал, что неплохо было бы сделать подборочку с наиболее актуальными и прикладными примерами с sed.

Уже когда начал её составлять вдруг подумал, а нужно ли сейчас вообще всё это? Как считаете? Мы находимся на сломе эпох. Сейчас же ChatGPT сходу приводит все эти команды и практически не ошибается в типовых задачах. Я до сих пор так и не начал им пользоваться. Пробовал несколько раз, но постоянно не использую. По привычке ищу решение в своих записях, чаще всего тут на канале.

Брюзжать, как старый дед, на тему того, что надо своими мозгами думать, а то совсем захиреют, не буду. ИИ помощники это уже наше настоящее и 100% наше будущее, как сейчас калькуляторы. Надо начинать осваивать инструментарий и активно использовать. Ну а пока вернёмся к sed 😁

🟢 Вырезаем всё, что между указанными строками в файле:

# sed -i.back '22826,26719 d' dump.sql

🟢 Вырезать первую и последнюю строки. Часто нужно, когда отправляешь в мониторинг вывод каких-нибудь консольных команд. Там обычно в начале какие-то заголовки столбцов идут, а в конце тоже служебная информация:

# sed -i.back '1d;$d' filename

🟢 Заменить в файле слово old_function на new_function:

# sed -i.back 's/old_function/new_function/g' filename

🟢 Если нужен более гибкий вариант поиска нужного слова, то можно использовать регулярные выражения:

# sed -i.back -r 's/old_function.*?/new_function/g' filename
# sed -i.back -r 's/^post_max_size =.*/post_max_size = 32M/g' php.ini

🟢 Удаляем комментарии и пустые строки:

# sed -i.back '/^;\|^$\|^#/d' php.ini

🟢 Вывести записи в логе веб сервера в интервале 10.01.2024 15:20 - 16:20. Удобная штука, рекомендую сохранить.

# sed -n '/10\/Jan\/2024:15:20/,/10\/Jan\/2024:16:20/p' access.log

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

# sed -i.back G filename
# sed -i.back '/pattern/G' filename

Про sed можно писать бесконечно, но лично я что-то более сложное редко использую, а вот эти простые конструкции постоянно. Чаще всего именно они используются в связке с другими консольными утилитами.

#bash #sed
​​Расскажу об одной возможности bash, с которой хоть и не часто, но приходится сталкиваться. Я столкнулся, когда писал утреннюю заметку про qemu-guest-agent. С его помощью можно выполнить любую команду на гостевой системе. Например так:

# qm guest exec 101 "df"

Далее мне захотелось добавить некоторые ключи к этой команде:

# qm guest exec 101 "df -Th"
Agent error: Guest agent command failed, error was 'Failed to execute child process “df -Th” (No such file or directory)'

Получил ошибку. Эта команда так не работает. Агент пытается найти бинарник df -Th. Читаю описание команды. Дополнительные аргументы надо передавать отдельно, примерно так:

# qm guest exec 101 "df" "/"

В таком виде работает. Добавляю ключи к df и опять ошибка:

# qm guest exec 101 "df" "/" "-Th"
Unknown option: th
400 unable to parse option

Тут типичная проблема для bash. Команда qm считает, что в конструкции -Th я передаю параметры для неё, а она их не понимает. Как же быть? Для этого существуют два тире --. Они говорят команде, что после них нужно игнорировать любые аргументы. То есть правильно будет вот так:

# qm guest exec 101 "df" "/" -- "-Th"

В таком виде команда отработает так, как ожидается. Двойным тире мы дали понять qm, что -Th не её аргументы.

С подобной проблемой легко столкнуться, когда, к примеру, вам через grep нужно найти строку -v. Как её указать? Это очень наглядный пример по данной теме. Grep будет считать, что -v это её аргумент и вместо поиска будет выводить свою версию. Тут надо действовать аналогично:

# grep -- -v file.txt

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

#bash
​​Если вам необходимо кому-то передать свой bash скрипт, но при этом вы не хотите, чтобы этот кто-то видел его содержимое, то есть простое решение. С помощью утилиты shc его можно транслировать в язык C и скомпилировать. На выходе будет обычный бинарник, который будет успешно работать практически на любой ОС Linux.

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

Shc живёт в базовых репозиториях Debian или Ubuntu, возможно и в других дистрибутивах. Для сборки также понадобится пакет gcc.

# apt install shc gcc

Пользоваться ей очень просто. Покажу на примере небольшого скрипта с вводом переменной в консоли.

#!/bin/bash
v=$1
echo "Simple BASH script. Entered VARIABLE: $v"

Запускаем:

# ./script.sh 111
Simple BASH script. Entered VARIABLE: 111

Теперь компилируем его в бинарник:

# shc -f -r script.sh

На выходе получаем два файла:
- script.sh.x - бинарник
- script.sh.x.c - исходный код

Запускаем бинарь:

# ./script.sh.x 123
Simple BASH script. Entered VARIABLE: 123

Отработал точно так же, как и bash скрипт. С помощью shc можно указать дату, после которой скрипт запускаться не будет. Выглядит это примерно так:

# shc -e 31/12/2023 -m "Извини, но ты опоздал!" -f -r script.sh
# ./script.sh.x
./script.sh.x: has expired!
Извини, но ты опоздал!

Я подозреваю, что прятать какие-то важные пароли таким образом опасно. Наверняка есть способ, чтобы его вытащить оттуда. Мне даже кажется, что это и не слишком сложно. В памяти то всё равно содержимое будет в каком-то виде отображаться. Можно сдампить память в момент запуска и посмотреть.

Быстро поискал и нашёл готовое решение по расшифровке таких файлов:
https://github.com/yanncam/UnSHc
Так что имейте ввиду, что это в основном защита от дурака.

#bash #script