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
Ещё пару месяцев назад мне посоветовали обратить внимание на книгу bash idioms, и, наконец, руки до неё дошли. Книга не слишком длинная, и всё же в неё поместилось описание значительной части той клинописи, которую можно встретить в скриптах Bash.
Чаще всего авторы ограничиваются парой примеров использования той или иной конструкции, отправляя читателя за деталями в документацию - формат хорошо себя зарекомендовал. Поскольку язык bash
не самый наглядный, авторы делятся полезными мнемониками, пару примеров которых я обязательно укажу в заметках.
Несколько раз читателя также предупреждают, что советы и темы, затрагивающиеся в главе, относятся исключительно к интерпретатору bash
, в некоторых случаях даже определённой версии. В книге не ставится цель обучить написанию переносимых скриптов - наоборот, предлагаемый код зачастую не поддерживается другими командными интерпретаторами, поэтому всегда важно обращать внимание, в каком окружении скрипт будет запускаться.
Посмотреть условия if
быстро можно посмотреть командой help test
.
Операторы &&
и ||
имеют равный приоритет.
В условиях if
в Bash лучше использовать двойные скобки [[
вместо одиночных [
. Их добавили относительно недавно, чтобы корректнее обработать некоторые ситуации. При этом [[
- ключевое слово Bash, [
- встроенная команда.
Лучше избегать идиомы if [ "x$var" = "x" ]
, встречающейся в старых скриптах, и использовать \-n
/\-z
в двойных скобках. При этом if [[ \-n "$var" ]]
эквивалентно if [[ "$var" ]]
.
Конструкция $(< file.txt)
эквивалентна конструкции $(cat file.txt)
, но немного быстрее.
При использовании переменной-счётчика в C-циклах лучше объявлять счётчик предварительно как declare \-i it
, чтобы избежать постоянных преобразований из строки в число.
Подоболочку можно использовать на месте обычной команды, например, так: echo 123 && { echo 321 }
.
${var\#a}
и ${var%a}
удаляют префикс и суффикс a
из переменной, соответственно. Запомнить можно так: на большинстве клавиатур символ \#
расположен левее символа %
:)
Единственное место, где можно встретить настоящие регулярные выражения в Bash - в контексте [[]]
. При этом само регулярное выражение не должно быть заключено в кавычки!
Переменная FUNCNAME
содержит стек вызовов функций Bash.
printf
- POSIX, echo
- нет! При этом builtin printf
поддерживает ключ \-v
, помещающий вывод команды в переменную.
Команда mapfile
читает файл в массив построчно.
В качестве shebang-строки лучше использовать не просто \#!/bin/bash
, а \#!/bin/bash \-
, чтобы избежать уязвимости с повышением привилегий.
Содержимое команды PS0
выводится сразу после нажатия на Enter, но перед запуском команды, COMMAND_PROMPT
- сразу после вывода PS1
.
Заметка дня #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-ов и рысканье по документации заметно проигрывают ему по скорости.
Во время работы на удалённом хосте по 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 ~ .
Заметка дня #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
. Мелочь, а приятно :)
Заметка дня #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
, который позволит посмотреть выбранный сертификат из цепочки. Своё решение оставлю в комментариях.
Telegram
Остальные 90%
Пару недель назад встретил короткую статью о вопросах, задаваемых при приёме в Google на позицию SRE, и был удручён, когда не смог найти ответ на одну из задач. В задании предлагалось обработать файл при помощи утилиты awk, а я всегда её пропускал мимо и…
За последнее время мне удалось добраться до многих книг, и о каждой хочется оставить подробный отзыв.
Чтобы не превращать канал в книжный клуб, попробую разбавить поток книжных заметок короткими мыслями и трюками, которыми хочется поделиться, однако на полноценную качественную заметку эти идеи пока не тянут - назовём рубрику "Заметками дня".
Помечать их буду тегом #tip. Если какая-то заметка натолкнёт вас на интересную мысль, если вы можете предложить вариант получше или же просто хочется обсудить идею, не стесняйтесь поделиться своим мнением в комментариях к заметке - так мы найдём самый классный и достойный способ решить задачу.
Итак, открываем рубрику первой Заметкой дня!
Неожиданно эта неделя обрадовала выходом моей публикации в журнале "IEEE Open Journal of the Communications Society" из первого квартиля:
https://ieeexplore.ieee.org/document/10547545.
Оформление и защита результатов длились весь прошедший год.
Исследование посвящено моделированию трафика приложений VR и было начато во время моей работы в Лаборатории беспроводных сетей ИППИ РАН. Безмерную благодарность выражаю коллегам, без помощи которых эта работа едва ли могла увидеть свет!
Статья находится в открытом доступе, поэтому с результатами исследования могут ознакомиться все желающие :)
Недавний выход стабильной версии 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
. А значит, можно дать этому пакетному менеджеру ещё один шанс :)
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