Остальные 90%

Description
Не так страшны первые 90% проекта, как оставшиеся 90%.

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

Автор: @korneev_es
ВК: https://vk.com/final90percent
Advertising
We recommend to visit
HAYZON
HAYZON
6,053,581 @hayzonn

لا اله الا الله محمد رسول الله

👤 𝐅𝐨𝐮𝐧𝐝𝐞𝐫: @Tg_Syprion
🗓 ᴀᴅᴠᴇʀᴛɪsɪɴɢ: @SEO_Fam
Мои каналы: @mazzafam

Last updated 3 weeks, 1 day ago

Architec.Ton is a ecosystem on the TON chain with non-custodial wallet, swap, apps catalog and launchpad.

Main app: @architec_ton_bot
Our Chat: @architec_ton
EU Channel: @architecton_eu
Twitter: x.com/architec_ton
Support: @architecton_support

Last updated 2 weeks, 2 days ago

Канал для поиска исполнителей для разных задач и организации мини конкурсов

Last updated 1 month ago

1 month ago

Ещё пару месяцев назад мне посоветовали обратить внимание на книгу bash idioms, и, наконец, руки до неё дошли. Книга не слишком длинная, и всё же в неё поместилось описание значительной части той клинописи, которую можно встретить в скриптах Bash.

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

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

  1. Посмотреть условия if быстро можно посмотреть командой help test.

  2. Операторы && и || имеют равный приоритет.

  3. В условиях if в Bash лучше использовать двойные скобки [[ вместо одиночных [. Их добавили относительно недавно, чтобы корректнее обработать некоторые ситуации. При этом [[ - ключевое слово Bash, [ - встроенная команда.

  4. Лучше избегать идиомы if [ "x$var" = "x" ], встречающейся в старых скриптах, и использовать \-n/\-z в двойных скобках. При этом if [[ \-n "$var" ]] эквивалентно if [[ "$var" ]].

  5. Конструкция $(< file.txt) эквивалентна конструкции $(cat file.txt), но немного быстрее.

  6. При использовании переменной-счётчика в C-циклах лучше объявлять счётчик предварительно как declare \-i it, чтобы избежать постоянных преобразований из строки в число.

  7. Подоболочку можно использовать на месте обычной команды, например, так: echo 123 && { echo 321 }.

  8. ${var\#a} и ${var%a} удаляют префикс и суффикс a из переменной, соответственно. Запомнить можно так: на большинстве клавиатур символ \# расположен левее символа % :)

  9. Единственное место, где можно встретить настоящие регулярные выражения в Bash - в контексте [[]]. При этом само регулярное выражение не должно быть заключено в кавычки!

  10. Переменная FUNCNAME содержит стек вызовов функций Bash.

  11. printf - POSIX, echo - нет! При этом builtin printf поддерживает ключ \-v, помещающий вывод команды в переменную.

  12. Команда mapfile читает файл в массив построчно.

  13. В качестве shebang-строки лучше использовать не просто \#!/bin/bash, а \#!/bin/bash \-, чтобы избежать уязвимости с повышением привилегий.

  14. Содержимое команды PS0 выводится сразу после нажатия на Enter, но перед запуском команды, COMMAND_PROMPT - сразу после вывода PS1.

1 month, 1 week ago

Заметка дня #6

Команда docker ps поддерживает ключ командной строки \-\-format, позволяющий уточнить вид, в котором выводится информация о контейнерах. Помимо встроенных форматов table и json команда позволяет использовать собственные Golang-шаблоны любой сложности.

man docker\-ps лениво ссылается на онлайн-документацию, где уже перечисляются доступные к выводу поля. Открывать каждый раз браузер и искать нужную секцию - долго, поэтому, чтобы подглядеть список полей, проще выполнить команду docker ps \-\-format json:

$ docker run \-\-name nginx \-d \-p 8080:80 nginx dac3e87...863bb93 $ docker ps \-\-format json | jq { "Command": "\"/docker\-entrypoint.…\"", "CreatedAt": "2024\-11\-10 17:16:13 +0300 MSK", "ID": "dac3e87abf28", "Image": "nginx", ... "Status": "Up 2 minutes" }

В отсутствие утилиты jq на хосте можно использовать Python - он-то наверняка будет:

$ docker ps \-\-format json | python3 \-m json.tool { "Command": "\"/docker\-entrypoint.\u2026\"", ... "Status": "Up 2 minutes" }

Интересно здесь, однако, другое: при написании своего шаблона мы можем подглядеть в объект, описывающий контейнер. Шаблон строится на основе структуры ContainerContext, содержащий поле c типа container.Summary. Все имена полей, указанные в документации, на самом деле являются вызовами одноимённых методов (например, так реализован .Image), определённых для структуры ContainerContext. Эти методы обращаются к полю c и вытаскивают оттуда соответствующую информацию, и при этом само поле не экспортируется и недоступно шаблонизатору. При попытке обратиться к нему возникнет ошибка:

$ docker ps \-\-format '{{ printf "%v" .c }}' failed to execute template: template: :1:15: executing "" at <.c>: c is an unexported field of struct type *formatter.ContainerContext

И всё-таки добраться до поля возможно: Golang позволяет распечатать всю структуру ContainerContext целиком, используя спецификатор %+\#v:

$ docker ps \-\-format '{{ printf "%+\#v" . }}'

Результат команды трудно назвать легкочитаемым: в одну строку будет выведено содержимое всей структуры, которое содержит заметно больше информации о контейнерах, чем показывают оригинальные фильтры.

К сожалению, вывести найденные поля в удобном виде всё равно не получится, поэтому в случае с командой ps трюк стоит отнести скорее к интересным фактам. Зато ключ \-\-format поддерживает множество других команд, и при написании фильтров, например, к команде docker inspect спецификатор %\#v может действительно пригодиться: вывод по умолчанию вываливает JSON-документ длиной в пару сотен строк, ориентироваться в котором непросто. С помощью же фильтра {{ printf "%+v" .Some.Path }} можно последовательно подбираться к нужному разделу документа:

$ docker inspect \-\-format '{{ printf "%\#v" . }}' nginx &types.ContainerJSON{...} $ docker inspect \-\-format '{{ printf "%\#v" .Config }}' nginx &container.Config{...} $ docker inspect \-\-format '{{ printf "%\#v" .Config.Entrypoint }}' nginx strslice.StrSlice{"/docker\-entrypoint.sh"}

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

#tip

1 month, 2 weeks ago

Во время работы на удалённом хосте по SSH часто возникает необходимость выполнить пару команд локально. Daniel J. Barrett в своей книге Efficient Linux at the Command Line советует на каждую новую задачу открывать новый терминал - этот подход решит проблему, однако не всегда: выполнив ещё одно подключение уже с удалённой машины, нам может потребоваться вернуться на неё же, а новый терминал открывается локально.

За примером далеко ходить не надо: предположим, мы хотим склонировать репозиторий Git на третьем хосте, имея ключ аутентификации на удалённой машине. Подключившись к последнему хосту, мы обнаружим, что не добавили ключ в ssh-агент, и за ним нужно вернуться. Конечно, можно так и поступить: набрав exit или ctrl+D, вернуться на удалённый хост, добавить ключ и подключиться снова, однако можно поступить элегантнее.

Команда ssh (по крайней мере, от OpenSSH) поддерживает несколько полезных комбинаций клавиш, о чём подробнее пишется в разделе Escape Characters странички man ssh. Среди них есть и нужная нам комбинация ~^Z, сбрасывающая текущую SSH-сессию в фон. Чтобы отправить эту последовательность, надо нажать клавиши Enter ~ Ctrl z, чтобы подключиться к сброшенной сессии - выполнить fg.

В примере выше поступить надо чуть хитрее: набрав ~^Z, мы свернём само подключение к удалённому хосту, а не то, которое мы открыли с него. В действительности нам нужно отправить комбинацию ~^Z на удалённый хост, набрав символ ~ дважды: Enter ~ ~ Ctrl z. Последовательность перемещений и вводимых команд будет выглядеть так:

```
# Connect to remote host: host2
host1 $ ssh host2

# Now go to the third host with ssh-agent forwarding
host2 $ ssh -a host3
host3 $ cd do/some/work/and/cd/deeply

# Forgot to add our key :(
host3 $ git clone [email protected]:ExAnimo/books.git
Cloning into 'books'...
[email protected]: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

# Indeed no key :(
host3 $ ssh-add -l
The agent has no identities.

# Return to host2 to get the key
# Enter ~ ~ Ctrl z
host2 $ ssh-add ~/.ssh/git_key

# Go back (workdir has not changed since ^Z!)
# and finish the work!
host2 $ fg
host3 $ git clone [email protected]:ExAnimo/books.git
Cloning into 'books'...
remote: Enumerating objects: 239, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 239 (delta 2), reused 0 (delta 0), pack-reused 230 (from 1)
Receiving objects: 100% (239/239), 51.48 KiB | 8.58 MiB/s, done.
Resolving deltas: 100% (76/76), done.
```

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

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

Наконец, говоря об escape-последовательностях ssh, нельзя обойти стороной последовательность ~.. Из-за проблем с сетью сессия может зависать, и ssh не отпустит терминал до тех пор, пока не наступит таймаут. По умолчанию наибольшее время ожидания соответствует TCP-таймауту, поэтому, чтобы не ждать час и не закрывать окно принудительно, гораздо удобнее просто прервать сессию, введя Enter ~ .

4 months ago

Заметка дня #2

Нередко возникает желание выполнить команду наподобие следующей:

$ sed 's/from/to/g' large\_file.txt > large\_file.txt

Результат выполнения в таком случае будет удручающий:

$ cat large\_file.txt $ du \-h large\_file.txt 0 large\_file.txt

Файл становится пустым ещё до начала выполнения sed. Командая оболочка, занимающаяся перенаправлением вывода, открывает файл при помощи системного вызова open(O_WRONLY|O_TRUNK), очищающего файл. Убедиться в этом можно, оттрассировав оболочку:

$ strace \-f \-e openat \-o trace.txt bash \-c '>asdf' $ cat trace.txt ... 283602 openat(AT\_FDCWD, "asdf", O\_WRONLY|O\_CREAT|O\_TRUNC, 0666) = 3 ...

Чтобы обойти эту особенность, проще всего создать временный файл, куда перенаправить вывод команды, и затем заменить им старый файл. Этот подход кому-то может показаться не эстетичным, и в такой ситуации пригодится команда sponge из пакета moreutils:

$ sed 's/from/to/g' large\_file.txt | sponge large\_file.txt

Задачу она решает довольно элегантно, но в дистрибутив может быть не включена по умолчанию.

К счастью, многие команды сразу имеют ключ, позволяющий выполнять операции над оригинальным файлом: так, например, вывод команды GNU sed, запущенной с ключом \-i/\-\-in\-place, будет автоматически оказываться в оригинальном файле. Под капотом опция всё равно создаёт временный файл - просто его переимнованием утилита занимается сама.

К слову, перенаправлением вывода можно пользоваться для создания файла: привычный всем touch file.txt можно сократить до >file.txt. Мелочь, а приятно :)

#tip

4 months, 2 weeks ago

Заметка дня #1

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

Оказывается, контейнер PEM может содержать комментарии до и после сертификата, поэтому вывод команды

$ openssl s\_client \-connect google.com:443 \-showcerts

можно направить прямиком в openssl x509:

$ echo | \ openssl s\_client \-connect google.com:443 \-showcerts 2>/dev/null | \ openssl x509 \-noout \-text Certificate: Data: Version: 3 (0x2) Serial Number: 71:8d:f8:a4:d1:48:8a:78:09:cc:ed:27:10:7d:81:84 ...

Вторая часть команды пропустит весь текст, окружающий пару \-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\- / \-\-\-\-\-END CERTIFICATE\-\-\-\-\-, и покажет содержимое первого сертификата.

Метод не подойдёт для вывода на экран информации о каждом из сертификатов в цепочке, однако имеющийся опыт показывает, что чаще всего как раз интересен именно первый из них.

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

#tip

Telegram

Остальные 90%

Пару недель назад встретил короткую статью о вопросах, задаваемых при приёме в Google на позицию SRE, и был удручён, когда не смог найти ответ на одну из задач. В задании предлагалось обработать файл при помощи утилиты awk, а я всегда её пропускал мимо и…

4 months, 2 weeks ago

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

Чтобы не превращать канал в книжный клуб, попробую разбавить поток книжных заметок короткими мыслями и трюками, которыми хочется поделиться, однако на полноценную качественную заметку эти идеи пока не тянут - назовём рубрику "Заметками дня".

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

Итак, открываем рубрику первой Заметкой дня!

6 months, 2 weeks ago

Неожиданно эта неделя обрадовала выходом моей публикации в журнале "IEEE Open Journal of the Communications Society" из первого квартиля:
https://ieeexplore.ieee.org/document/10547545.
Оформление и защита результатов длились весь прошедший год.

Исследование посвящено моделированию трафика приложений VR и было начато во время моей работы в Лаборатории беспроводных сетей ИППИ РАН. Безмерную благодарность выражаю коллегам, без помощи которых эта работа едва ли могла увидеть свет!

Статья находится в открытом доступе, поэтому с результатами исследования могут ознакомиться все желающие :)

6 months, 2 weeks ago

Недавний выход стабильной версии Ubuntu 24.04 LTS заставляет задуматься о неминуемом окончании поддержки полюбившейся версии 22.04. Релизный цикл Ubuntu длится 5 лет, торопиться с обновлением пока нет необходимости, однако на новые сервера уже стоит устанавливать последнюю LTS-версию.

Взятый Canonical курс на использование пакетного менеджера snap одобряют не все пользователи Ubuntu. Несмотря на обещанные преимущества snap, deb-пакеты остаются привычным решением, которое хочется вернуть. Это и сделаем.

Сам snap установлен как deb-пакет, от которого можно избавиться, предварительно удалив все имеющиеся snap-ы:

```
# Remove dependent snaps first
while snap list 2>&1 | grep -v 'No snaps'; do
for pkg in $(snap list | cud -d' ' -f1 | tail -n +2); do
sudo snap remove "$pkg"
done
done

# Remove snap
sudo apt -y purge snapd
sudo apt -y autoremove
```

На этом упражнение не заканчивается. Многие deb-пакеты ссылаются на snap-пакеты, и в зависимостях содержат только что удалённый snapd, который вернётся при установке. Застрахуемся от этого, создав файл /etc/apt/preferences.d/snap\-disable со следующим содержимым:

Package: snapd Pin: release a=* Pin\-Priority: \-10

Теперь установка пакетов, зависимых от snapd, будет завершаться ошибкой:

$ sudo apt install firefox ... The following packages have unmet dependencies: firefox : PreDepends: snapd (>= 2.54) but it is not installable ...

Мы успешно удалили пакетный менеджер, однако также мы потеряли Firefox и утилиты, завязанные на snap. К счастью, многие из них можно найти в обычных PPA-репозиториях и поправить приоритеты установки тем же способом, которым мы запретили установку snapd. На примере Firefox это будет выглядеть так:

```
$ cat /etc/apt/preferences.d/firefox-nosnap
# Always use ppa repo
Package: firefox*
Pin: release o=LP-PPA-mozillateam
Pin-Priority: 1001

# Ignore snap version
Package: firefox*
Pin: release o=Ubuntu
Pin-Priority: -1

$ sudo add-apt-repository -y ppa:mozillateam/ppa
$ sudo apt install firefox
```

Теперь всё готово, мы окончательно избавились от snap и можем установить Firefox привычной командой.

В качестве заключения отмечу, что самый весомый аргумент против snap в Ubuntu 24.04 перестал быть актуальным: snap по-прежнему монтирует пакеты, однако вывод df теперь чист: все точки монтирования попадают в отдельное пространство имён, и найти их можно, например, командой nsenter \-a \-t $(pgrep firefox) df. А значит, можно дать этому пакетному менеджеру ещё один шанс :)

6 months, 3 weeks ago
We recommend to visit
HAYZON
HAYZON
6,053,581 @hayzonn

لا اله الا الله محمد رسول الله

👤 𝐅𝐨𝐮𝐧𝐝𝐞𝐫: @Tg_Syprion
🗓 ᴀᴅᴠᴇʀᴛɪsɪɴɢ: @SEO_Fam
Мои каналы: @mazzafam

Last updated 3 weeks, 1 day ago

Architec.Ton is a ecosystem on the TON chain with non-custodial wallet, swap, apps catalog and launchpad.

Main app: @architec_ton_bot
Our Chat: @architec_ton
EU Channel: @architecton_eu
Twitter: x.com/architec_ton
Support: @architecton_support

Last updated 2 weeks, 2 days ago

Канал для поиска исполнителей для разных задач и организации мини конкурсов

Last updated 1 month ago