Bash Days | Linux | DevOps
23.6K subscribers
232 photos
26 videos
781 links
Авторский блог от действующего девопса

Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.

Автор: Роман Шубин
Реклама: @maxgrue

MAX: https://max.ru/bashdays

Курс: @tormozilla_bot
Блог: https://bashdays.ru
Download Telegram
В Linux есть масса утилит, для поиска файлов. К примеру всем известный монструозный find, который умеет даже тапочки приносить.

Но что, если мне нужно просто найти какой-то файл? Использовать find для такой простой задачи, это явно стрельба из пушки по воробьям. Для таких задач, как моя, есть более изящные решения.

И это решение, утилита locate.

Утилита locate используется для поиска файлов, расположенных на машине пользователя или на сервере. Фактически она выполняет ту же работу, что и команда find, однако, ведёт поиск в собственной базе данных.

Ключевая фраза здесь - в собственной базе данных. То есть применяя утилиту locate, поиск файлов будет осуществляться не по файловой системе, как это делает find. А будет использоваться собственная база данных. Коротая позволяет искать файлы со скоростью света.

Обновление базы данных locate происходит автоматически, как правило, раз в сутки. Либо можно запустить руками командой updatedb.

Давай теперь потыкаем, запускай:

locate txt

Ищем все файлы, имена которых содержат «txt». Опа, меньше чем за секунду команда нашла все подходящие файлы, которые можно было найти. А если сделать это через find, то это у меня это заняло 18 секунд. Разница очень ощутимая.

Ищем файлы, которые оканчиваются на «txt»

locate '*txt' 

А теперь давай посчитаем общее количество файлов, которые нашлись

locate -ic '*txt' 

Ключ -i = режим регистронезависимости
Ключ -с = вывести общее количество найденных файлов

Полезли в кишки, заводим strace.

Переходим в папку cd /tmp и создаем подопытный файл: > hello.txt

Знал же что пустой файл можно создать через символ «>»? Если нет, то теперь знаешь.

Так, я нахожусь в папке tmp и у меня создан файл hello.txt запускаю:

locate *txt

Опа, ничего нет, как так? Мы же уже запускали ранее это, чтобы найти все файлы, которые заканчиваются на txt. Что случилось?

Мы вызываем внешнюю команду locate, но вызываем мы ее из интерпретатора bash. Вернее её вызывает сам интерпретатор. И это накладывает свои нюансы. В нашем случае этот нюанс «Подстановка имен файлов», то есть Globbing.

Сейчас будет сложно для понимания, но чуть ниже я разжую на человеческий.

Во времена UNIX V6, существовала программа /etc/glob, которая могла раскрывать шаблоны подстановки. Очень скоро она стала встроенной функцией командной оболочки.

Но что же случилось то в итоге, почему ничего не нашлось? А произошло следующее: после того, как интерпретатор обнаружил символ «*», который является спецсимволом и соответствует любой строке, интерпретатор попытался сделать подстановку Globbing и ему это удалось.

И при вызове команды locate она получила в качестве аргументов, результат этих подстановок. Давай посмотрим наглядно что произошло. Запускаем:

strace -e execve locate *txt 

На выходе получаем:

execve("/usr/bin/locate", ["locate", "hello.txt"], 0x7ffe242252d8 /* 27 vars */) = 0

Опция «e» и аргумент «execve» сообщают strace, что я хочу отслеживать только системные вызовы «execve».

execve() выполняет программу, заданную параметром filename

Возвращаемся к выводу от strace и видим что locate получила в качестве аргумента hello.txt

И теперь locate будет искать файлы именно по этому шаблону, а не по тому, что мы с тобой ожидали, когда писали «*txt».

Вот лишь по этому на экран ничего не вывелось, когда я запустил locate *txt

А чтобы этого не происходило всегда используй quoting (кавычки) и будешь получать ожидаемый результат.

locate '*txt' 

Механизм подстановки имен файлов (Globbing) на самом деле удобен, просто нужно использовать его по назначению.

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

Поигрались и хватит. Пошли дальше работу работать. Всех был рад видеть, пока-пока!

tags: #bash #strace #debug
—
💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1572
Service или Systemctl?

Каждый стартует или стопает процессы как привык. Во времена динозавров когда еще не существовало systemd, всё сводилось к командам:

service apache restart


Сейчас мир изменился, появился systemd, но привычки остались прежними.
ㅤ
Теперь сервисами рекомендуется управлять через systemctl.

Но какая в хуй разница как управлять сервисам?

И service и systemctl выполняют по сути одно и тоже и ты по итогу получаешь ожидаемый результат.

На самом деле сейчас команда service это обёртка вокруг systemctl.

Давай убедимся:

strace -f service nginx restart 2>&1 | grep execve


По итогу ты получишь:

execve("/usr/bin/systemctl", ["systemctl", "restart", "nginx.service"])


То есть service обратился к systemctl и произвел перезапуск nginx.

А если глянуть исходник service:

cat /usr/sbin/service


То ты увидишь обычный bash скрипт в котором и происходит вся эта магия.

Если система использует SysVinit или Upstart (например, старые версии Debian, CentOS 6, Ubuntu 14.04), то service будет работать напрямую со скриптами /etc/init.d/.

По бест-практикам старайся использовать systemctl. Но опять же service никто не запретит тебе использовать.

Хотя когда молодые ребята видят как ты вводишь service, у них разрывает жопу и ты автоматически присваиваешь себя к престарелым скуфам.

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

Кроме systemctl и service есть такие штуки для управления процессами:

/etc/init.d/nginx restart
rc-service nginx restart
sv restart nginx
s6-rc -d change nginx
supervisorctl restart nginx


А чтобы проверить какая система инициализации у тебя, вот тебе пиздатая команда:

ps -p 1 -o comm=


Вот и вся наука.

А как ты рулишь сервисами? Как скуф или как современный молодой человек?

tags: #linux #debug #strace
—
🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
91
Если ты выполняешь план, то тебе повысят план, а не зарплату!

Пытался вчера подружить taskwarrior и s3 для синхронизации, из коробки оно вроде как только с AWS работает.

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

sync.backend=s3
sync.aws.region=ru-7
sync.aws.bucket=taskwarrior
sync.aws.access_key_id=aed38518013b4ab
sync.aws.secret_access_key=992570bad57
sync.aws.endpoint=s3.ru-7.storage.selcloud.ru


Проверяю task sync init, хуй там плавал, ошибка:

unhandled error: dispatch failure: io error: error trying to connect: dns error: failed to lookup address information: Name or service not known: dns error: failed to lookup address information: Name or service not known: failed to lookup address information: Name or service not known


Мде… Всёж правильно, эндпоинт пингуется, курлится, телнетится. Описывать весь момент дебага не буду, но там конкретный такой - метод тыка был.

Ну раз обычный «метод тыка» не помогает, расчехляем strace!

strace -s 200 -f -e trace=network,connect,sendto,recvfrom task sync


Что делает команда:

-s 200 — печатать до 200 байт строковых аргументов (по умолчанию strace режет строки до 32 байт). Это важно, чтобы увидеть полный URL/hostname, который передаётся в syscalls.

-f — следить не только за основным процессом, но и за всеми дочерними (fork/clone)

-e trace=network, connect, sendto, recvfrom — ограничиваем вывод только сетевыми вызовами: socket, connect → создание сокетов и подключения (TCP/UDP). sendto / recvfrom → передача данных (обычно видно DNS-запросы, HTTP-заголовки и т.д.).

И в выхлопе находим строчку: taskwarrior.s3.ru-7.amazonaws.com.

😀😃😄😁😅😂🤣😊
😇🙂🙃😉😌😍🥰😘
😗😙😚😋😛😝😜🤪
🤨🧐🤓😎🤩🥳😏😒
😞😔😟😕🙁☹️😣😖
😫😩🥺😢😭😤😠😡

Ну ёб твою мать! А нахуй я тогда все эти приседания с конфигом устраивал, если эта падла хуй положила и по task diagnostic никаких ошибок не вывело.

То есть настройка sync.aws.endpoint=… вообще не учитывается — клиент жёстко строит URL по схеме AWS.

Ну хоть проблему нашли. Strace все же достаточно пиздатый инструмент.

Отсюда вывод: с кастомным S3 напрямую taskwarrior работать не сможет. Даже если устроить подмену хостов или сделать хак через CNAME.

А как синхронизировать-то задачи? Ооо брат, я уже написал про это отдельный пост, чуть позже закину.

Хороших тебе выходных!

🛠 #strace #debug #taskwarrior
—
✅ @bashdays ✅ @linuxfactory ✅ @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
1358
А у нас сегодня на повестке интересная «темка» — Автоматическая очистка tmp-файлов даже после SIGTERM.
ㅤ
Как говорится — Волков бояться, в лесу не ебаться!


Пишем подопытный скрипт:

#!/usr/bin/env bash
set -e

temp=$(mktemp)
echo "Создал файл: $temp"

false

echo "Удаляю файл"
rm -f "$temp"


Чмодим, запускаем, ага скрипт упал на false, но успел создать временный файл в папке /tmp == /tmp/tmp.oWQ0HiYxSV. И он не удалился. Скрипт вернул статус == 1;

Код выходы проверить так:

echo "код: $?"


Здесь я взял синтетику, показать наглядно этот случай. Суть «темки» — подчищать за собой, при любых обстоятельствах, даже если скрипт вернул ошибку, либо завершился успешно.

Снова изобретать велосипед? Неа! Всё это работает из коробки.

#!/usr/bin/env bash
set -e

temp=$(mktemp)
echo "Создал файл: $temp"

trap 'echo "Удаляю $temp"; rm -f "$temp"' EXIT

false


В этом случае скрипт также вернет код 1, но временный файл будет удалён при любом раскладе.

Нихуя непонятно, но очень интересно.

Смотри — trap вызывает rm ПРИ ВЫХОДЕ ИЗ СКРИПТА. И похуй с каким статусом был завершен скрипт. Временный файл будет гарантированно удалён.

Давай подключим strace:

strace -e trace=process,signal bash -c '
tmp=$(mktemp)
trap "rm -f $tmp" EXIT
echo "tmp=$tmp"
'


В выхлопе видим:

openat(AT_FDCWD, "/tmp/tmp.Dn2qG3uK9V", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
rt_sigaction(SIGINT, {sa_handler=0x...}, NULL, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=0x...}, NULL, 8) = 0
unlink("/tmp/tmp.Dn2qG3uK9V") = 0
exit_group(0) = ?


openat(... O_EXCL) — это mktemp, создающий файл атомарно
rt_sigaction() — Bash ставит ловушки для сигналов
unlink() — это и есть выполнение trap
exit_group(0) = ? — завершает процесс

То есть Bash вызывает rm/unlink() ДО выхода из процесса.

Почему trap срабатывает?

Если скрипт упал или получил сигнал kill -TERM <pid>, то на уровне Bash:

— в обработчике сигнала вызывается registered trap
— затем Bash падает/выходит
— trap УЖЕ успел выполнить команду очистки

trap выполняется ДО того, как процесс отдаст SIGTERM ядру, если только ловушка для этого сигнала была установлена.

Да банально при выполнении скрипта ты нажал CTRL+C, скрипт остановился, НО временные файлы сразу подчистились.

➡️ Про exit codes (коды выхода) писал тут.
➡️ Про сигналы писал ранее тут и тут и тут и тут.


Вот такие пироги!

С пятницей и хороших тебе предстоящих выходных!

🛠 #bash #debug #strace
—
💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
559
Вчера в чатике интересный комментарий пролетел:

Если открыть файл в vim, то lsof покажет этот файл открытым. А вот если открыть файл в nano, то lsof не факт, что покажет этот файл открытым. Ну видимо nano не постоянно держит открытым файл.


Давай покопаем глубже и посмотрим что происходит. Расчехляем лабу!
ㅤ
В первом терминале запускаем:

echo "secret data" > test.txt
nano test.txt


Во втором запускаем глазища:

sudo strace -e open,openat,write,close -p $(pgrep nano)


Пока пусто. Теперь в первом терминале нажимаем CTRL+O и смотрим выхлоп во втором терминале:

openat(AT_FDCWD, "test.txt", O_WRONLY|O_TRUNC) = 3  
write(3, "secret data\n", 5) = 5
close(3) = 0


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

Теперь идем в vim и повторяем предыдущие команды:

vim test.txt


Но во втором терминале запускаем:

watch -n 0.01 "lsof | grep test.txt"


И сразу же видим что lsof показывает открытые файлы:

vim 6423 user 5u REG 8,48 12288 11148 /tmp/.test.txt.swp
vim 6423 6424 vim user 5u REG 8,48 12288 11148 /tmp/.test.txt.swp


Но это не сам файл test.txt, это его swap версия, то есть создается еще один файл. Если открыть такой файл, то в нем будет мусор:

0000000 3062 4956 204d 2e39 0031 0000 1000 0000
0000010 bfdc 69a7 b9a1 0000 1917 0000 7375 7265
0000020 0000 0000 0000 0000 0000 0000 0000 0000


Этот файл, vim использует для crash recovery, undo history, file already open, incremental writes и т.п. Получается что vim постоянно держит открытый swap, а не исходный файл.

Если выполнить:

lsof -p 6423


То увидим всю подноготную этого процесса включая дескрипторы.

Ну и финт ушами. В первом терминале выполняем:

vim test.txt


Во втором:

mv test.txt test.old


И видим что vim грязно выругался: E211: File "test.txt" no longer available.

А если все то же самое проделать с nano то он даже не пискнет, потому что файл загружен в память и ему похеру что мы физически снесли файл с диска. Можно снова нажать CTRL+O и он даже предложит нам его сохранить со старым именем.

Вот тут уже можно почесать репу, получается nano работает быстрее чем vim? Неа, vim тоже загружает файл в память (buffer ram). А swap файл это всего лишь журнал изменений либо undo-файлы. Swap не заменяет память, он выступает в роле дополнительного инструмента.

Схема для vim такая:

disk file → RAM buffer (vim)
↓
swap file (.swp)


Схема для nano попроще:

disk file → RAM buffer


В nano нет ни рекавери, не другой подобной хуйни-муйни. Ну а по скорости, разницы практически нет (если файлы небольшие), файлы читаются один раз, оба редактора работают в памяти, ну а linux page cache делает чтение еще быстрее.

Если файлы большие, vim все же предпочтительнее, у него более продвинутый механизм по работе с буферами.

Так и живем!

🛠 #debug #strace
—
💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
181