Линукс и DevOps Дни
2.23K subscribers
108 photos
8 videos
194 links
Самобытно про разработку, devops, linux, скрипты, тестирование, сисадминство, техдирство, пиэмство и за айтишную жизу.
Download Telegram
С утра успешно победил всех антагонистов и теперь самое время сделать что-то полезное.

Сегодня разбираем проблемный Bash скрипт.

#!/bin/bash  

i=0
for name in `ls *.txt`
do
i=$[i+1]
mv $name `printf "%02d" $i`.txt
done


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

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

Для начала, давай создадим вводные, чтобы было с чем работать.

mkdir /tmp/test
cd /tmp/test
touch {001..003}.txt {01..03}.txt


Так, создали 6 файлов с разными именами:

01.txt
02.txt
03.txt
001.txt
002.txt
003.txt


Модифицируем проблемный скрипт, чтобы он выводил строку с командой mv перед её выполнением:

#!/bin/bash  
i=0
for name in `ls *.txt`
do
i=$[i+1]
echo 'DEBUG: mv' $name `printf "%02d" $i`.txt
mv $name `printf "%02d" $i`.txt
done

exit 0


Запускаем и получаем красивое:

DEBUG: mv 001.txt 01.txt
DEBUG: mv 002.txt 02.txt
DEBUG: mv 003.txt 03.txt
DEBUG: mv 01.txt 04.txt
DEBUG: mv 02.txt 05.txt
DEBUG: mv 03.txt 06.txt


Смотрим что у нас осталось, хуяк и вместо 6 файлов, у нас осталось всего лишь 3 штук.

ls

04.txt
05.txt
06.txt


Куда блядь делось остальное? Вокруг да около ходить не будем.

Дело в том, что в одном каталоге не может быть двух файлов с одинаковым названием. Очевидно? Да!

Как работает mv:

mv <старое имя> <новое имя>


Если <новое имя> это существующее имя файла, тогда оно будет удалено, а <старое имя> получит имя <новое имя>.

Если взять нашу ситуацию, то произошло следующее:

1. DEBUG: mv 001.txt 01.txt 
2. DEBUG: mv 002.txt 02.txt
3. DEBUG: mv 003.txt 03.txt
4. DEBUG: mv 01.txt 04.txt
5. DEBUG: mv 02.txt 05.txt
6. DEBUG: mv 03.txt 06.txt


1. Удалено 01.txt. и 001.txt -> 01.txt
2. Удалено 02.txt. и 002.txt -> 02.txt
3. Удалено 03.txt. и 003.txt -> 03.txt

Итого потеряли 3 файла, печаль.

Теперь давай более наглядно. Удаляем подопытные файлы и создаем новые. Плюс запишем в каждый файл его имя.

for i in {001..003}.txt {01..03}.txt; do echo $i > $i;done


Получилось 6 файлов, в каждом файле содержится имя самого файла.

Смотрим что получилось:

ls -la

8 Jul 10 08:05 001.txt
8 Jul 10 08:05 002.txt
8 Jul 10 08:05 003.txt
7 Jul 10 08:05 01.txt
7 Jul 10 08:05 02.txt
7 Jul 10 08:05 03.txt


Снова модифицируем скрипт и задаем опцию -b для утилиты mv. Теперь для существующего файла будет создаваться резервная копия. Бекап ёпта!

#!/bin/bash  
i=0
for name in `ls *.txt`
do
i=$[i+1]
echo 'DEBUG: mv' $name `printf "%02d" $i`.txt
mv -b $name `printf "%02d" $i`.txt
done
exit 0


Выполняем скрипт и смотрим что у нас осталось от файлов.

ls -la

7 Jul 10 08:05 01.txt~
7 Jul 10 08:05 02.txt~
7 Jul 10 08:05 03.txt~
8 Jul 10 08:05 04.txt
8 Jul 10 08:05 05.txt
8 Jul 10 08:05 06.txt


Теперь смотрим содержимое самих файлов и преисполняемся.

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

Ну и всегда обрабатывай исключения если файл уже существует, чтобы не остаться без штанов.

Изучай, всего тебе самого наилучшего!

tags: #linux #bash

🔔
Привет. Сегодня нассым в чайник.

А если конкретнее, рассмотрим более подробно команду man

Это с виду она дохуя скучная, на деле там всё по полочкам.

Man содержит разделы от 1 до 9 + дополнительные (n, l, p). В дополнительные разделы входят всё то, что по каким-то причинам не попало в первые 9.

Все это дело сконфигурировано в файле: /etc/manpath.config, в него можно не смотреть, там хуйня какая-то непонятная для не подготовленного человека.

Ну дак вот. В каждом man разделе, есть страница intro. То бишь «вступление», которое описывает что содержит раздел.

Давай тыкать палкой.

Ставим пакет:

apt install manpages-dev


Запускаем:

man 1 intro


Выведется список с содержимым 1го раздела:

- Login
- The shell
- Pathnames and the current directory
- Directories
- Disks and filesystems

и т.п.


Теперь пройдемся по разделам:

1. Справка по стандартным командам (программы и утилиты). Это самый огромный раздел в справочной системе. Глаза разбегутся. Но есть лайкфак, можно воспользоваться поиском.

man -k copy


Выведется ограниченный список команд и утилит, которые содержат слово «copy»

cp (1) - copy files and directories
cpgr (8) - copy with locking the given file
cpio (1) - copy files to and from archives
cppw (8) - copy with locking the given file
dd (1) - convert and copy a file


В скобках выводится цифра раздела, в котором была найдена справка. А дальше можно сделать например так:

man 8 cppw


Херак и ты получил желаемое. С поиском намного удобнее и приятнее работать.

2. Раздел содержит описание системных вызовов. В этом разделе можно найти понимание этих ваших линуксов. Например, смотрим справку по mkdir.

man 2 mkdir


Кончаем и получаем список системных вызовов с которыми работает mkdir.

3. Раздел с библиотечными функциями. Тут больше для программистов.

4. Описание файлов-устройств расположенные в /dev

5. Описание форматов файлов. Конфигурационные файлы приложений и их синтаксис. Например, синтаксис файла теневых паролей.

man 5 login.defs


Чтиво весьма занятное. Именно в этом файле задаются «SYS_UID_MAX», «SYS_UID_MIN». Это диапазон UID, который будет присвоен пользователю, при использовании утилит «useradd» или «newusers» когда, ты добавляешь нового пользователя.

6. Раздел игр.

7. Описание стандартов и соглашений. Например, последовательность загрузки ОС.

man 7 boot


или параметры ядра при загрузке:

man 7 bootparam


или шаблоны имён файлов:

man 7 glob


8. Команды для системного администрирования. Например, информация о портах/сокетах:

man 8 netstat


монтирование устройств:

man 8 mount


отображение информации о дисках и их настройка:

man 8 hdparm


9. Функции ядра. В большинстве случаев этот раздел отсутствует в современных дистрибутивах. Раздел не является стандартом. Если нужно узнать про функции ядра, тут только поиск через man -k <ключевое слово>.

Так же ты можешь встретить разделы n, l, p, но опять-же они не всегда присутствуют, все зависит от того, какой софт ты ставил себе на машину.

Ну и закончим эту простыню вишенкой. Для этой команды нужны Иксы.

apt install man-db groff
man -H bash


Страница со справкой Bash будет отформатирована в «html» формате и открыта в браузере по умолчанию.

Без Иксов просто сформируется «html» файл в папке tmp, у меня он появился тут: /tmp/hmanU3yORv/bash.html

Ладно, изучай. Увидимся завтра!

PS: Все кто подал заявки на вписку в Linux factory, все ок. Как только решу вопросы с эквайрингом Тибанка (бекенд у них отвалился), всем напишу. Терпения вам и мне дорогие подписчики. Всех обнял!


tags: #linux #bash

🔔
Ёпта! Работа над Linux Factory идет полным ходом, всем кто принес мне свой емейл в бота, все котики и записаны.

Кому я еще не ответил — отвечу, ничо не потерялось. Я тут в одну харю ща всё разгребаю и отбиваюсь от менеджеров тибанка с помощью их же разработки — автоответчика Олега.

В начале следующей неделе буду рассылать инвайты в закрытый канал, ну и инструкции чо каво делать.

✔️ Так, теперь по сегодняшней теме — Awk и пасхальное яйцо.

Если запустить:

awk --nostalgia


Всё нахуй упадет с ошибкой — awk: bailing out near line 1 Aborted

nostalgia это не документированная опция, которая была добавлена в версии 2.11 beta и сопровождалось странным комментарием:

undocumented feature, inspired by nostalgia, and a T-shirt


Чо за херня?

Ранние версии awk аварийно завершались при различных эксепшенах выводя подобную ошибку. Утилиту отладили, но осталась — ностальгия. Но кто знает, возможно это ключ к ответу на главный вопрос жизни.

Еще из интересного. Gawk использует GNUтое расширение предоставляемое функциями разбора аргументов командной строки. При активации этого расширения доступен специальный синтаксис -W long_option.

awk -Wnos
awk -Wcopy


Эти команды аналогичны:

awk --nostalgia
awk --copyright


Странное конечно поведение учитывая содержимое этого поста.

Ну да ладно, хуй с ним. Чо только не придумают…

Хороших тебе предстоящих выходных и береги себя.

Кстати кто сегодня винду обновил и получил синьку, самое быстрое решение опубликовал Дима в полезняшках. Репостить не буду, кому надо сами сходите посмотрите.


Ааа еще забыл, Tagd Tagd написал нам две пиздатых статьи, на днях выложу. Спасибо Tagd Tagd с меня бонусы!

tags: #linux #bash

🔔
Сегодня мы рассмотрим второй способ, как запилить SOCKS через ssh.

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

Как-то видел анимацию сисек при долгом копировании, но сейчас не нашел. Может кто подскажет... Варианты анимации здесь.

sudo vim /usr/local/bin/socksshell

#!/bin/bash
trap 'echo -e "\\nWaiting session end...";exit' SIGINT SIGTERM SIGHUP SIGQUIT
printf "VPN on. Press CTRL-C for BREAK "
while :;do
for i in '> ' '>>' ' <' '<<';do
printf "\b\b$i"
sleep 3
done
done

sudo chmod 755 /usr/local/bin/socksshell


Скрипт не дает пользователю ничего делать, но своей анимацией поддерживает соединение в рабочем состоянии. Если не нравится shell на скрипте - поищите в сети Sleep Dummy Shell он на си.

В файле /etc/ssh/sshd_config

Match User bashdays_tagd
MaxAuthTries 3
MaxSessions 10
PasswordAuthentication no
PermitEmptyPasswords no
PubkeyAuthentication yes
allowtcpforwarding yes
ForceCommand /usr/local/bin/socksshell
#--


Опция ForceCommand, фактически, заменяет оболочку для ssh, если захотите заменить оболочку пользователя в глобально системе (не обязательно):

sudo usermod -s /usr/local/bin/socksshell bashdays_tagd
sudo service sshd restart


А на клиенте:

ssh -D 5000 bashdays_tagd.host.name


После запуска и ввода фразы ключа программа выводит анимацию. Если прервать команду, соединения в браузере не разорвутся, как в первом способе, а программа будет ожидать закрытия браузера!!!

В firefox Настройки -> Настройка сети -> Параметры соединения: Ручная настройка прокси. SOCKS5. 127.0.0.1 порт 5000.

+ галка Отправлять DNS-запросы через прокси при использовании SOCKS5.

или

ssh -D 192.168.1.57:5000 bashdays_tagd.host.name


Если ваш адрес 192.168.1.57 и вы человек еще более добрый чем я и решили поделиться вашем счастьем со всей своей локалкой.

В настройка firefox нужно будет заменить 127.0.0.1 на 192.168.1.57

Продолжение следует... © by Tagd Tagd

tags: #linux #networks #bash

🔔
Когда собирался писать об этом операторе на ум пришла считалочка, которой меня научили в пионерском лагере.

А поскольку я сейчас не матерюсь, чтоб никто не догадался, перейду на старославянский алфавит.

Итак: Шёл Хер по херу, встретил Хера на херу, взял Хер Хера за хер, и к херам закинул нахер.

Вот, а сейчас говорят, что пионеров ничему хорошему не учили... exec.

Есть два варианта использования:

1. exec command — билет в один конец. Для чего это нужно я так и не понял, может умные в комментах напишут. Перед выполнением команды, bash фактически завершается, управление передаётся команде. И все...

пример:

exec echo 123 — результат очень красочный. Такой же, как и с любой другой командой.

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

В Basic open "filename.txt" for input as #3
В bash exec 3<fileneme.txt
В Basic open "filename.txt" for output as #4
В bash exec 4>filename.txt
В Basic open "filename.txt" for append as #5
В bash exec 5>>filename.txt
В Basic open "filename.txt" for random as #6
В bash exec 6<>filename.txt

Приведу несколько полезных примеров

exec 2>/dev/null — все сообщения об ошибках отправить по пути, указанному в считалочке.

На код ошибки (типа errorlevel) это не влияет.

exec 1>filename.txt все сообщения, кроме ошибок отправить в файл.
exec 1>filename.txt 2>&1 все сообщения, включая ошибки отправить в файл.

Стоп-стоп. Откуда взялся &? Легко запомнить. Это костыль. Если вы не укажите &, сообщения попадут в файл с именем "1".

Все помнят, что 0=/dev/stdin 1=/dev/stdout 2=/dev/stderr. Остальные 3-9 можем использовать по собственному усмотрению.

Пример:

#!/bin/bash
seq 1 3|tee 1.txt > 2.txt
exec 3<1.txt
while read -u3 L1;do
exec 4<2.txt
while read -u4 L2;do
echo $L1 $L2
done
done


seq — создаем два файла, по три строки в каждом
exec 3<1.txt - открываем файл для чтения с дескриптором 3
read -u3 L1 читаем из файла с дескриптором 3. Дальше все аналогично.

Если нужно закрыть 4, например, дескриптор за ненадобностью exec 4>&-

man tee seq
help while read exec


tags: #bash © by Tagd Tagd

🔔 ➡️
Привет. В дополнение к предыдущему посту про свистоперделки.

Чтобы каждый раз не вводить такую дичь:

ssh -i ~/.ssh/id_rsa user.xxx.xxx.xxx


Ключик можно автоматически добавлять в ssh-agent и тогда подключение будет выглядеть так:

ssh user.xxx.xxx.xxx


Чтобы это провернуть, прописываем в ~/.profile

if [ -z "$SSH_AUTH_SOCK" ]; then
eval $(ssh-agent -s) > ~/.ssh/ssh-agent
fi

ssh-add ~/.ssh/gitlab_rsa
ssh-add ~/.ssh/production_rsa
ssh-add ~/.ssh/home_rsa


Первый блок проверяет запущен ли SSH агент, а если нет — запускает его.

Второй блок добавляет нужные ключи в агент. Посмотреть все ключи в агенте можешь командой ssh-add -L, а удалить всё из агента можно командой ssh-add -D.

Вот и вся наука. Ну а теперь интерактивная часть.

Ну а чтоб тебе не скучно было, можешь написать башник и скинуть в комменты, который автоматически пробегается по всем ключам в папке ~/.ssh и добавляет их в агент.

Но учти что в этой папке могут быть косячные ключи, ключи с неправильными правами, да и всякие файлы вида config, known_hosts. Короче потребуется работа с эксепшенами.

Чей коммент с решением соберет больше всех реакций и будет заебись работать, закину приятный бонус. Поехали!

tags: #utilites #рабочиебудни #bash

🔔 ➡️
Здрасти. К делу!

Гиту откровенно насрать на права доступа файлов. И после клонирования репы, эти права будут установлены по umask пользователя.

Ситуация не критичная, но иногда случаются моменты когда нужно все эти права сохранить и после клона восстановить.

На помощь приходит костыль, всё в лучших традициях!

#!/bin/bash

find . -type f -exec stat --format='%a %n' {} \; > permissions.txt
git add permissions.txt
git commit -m "на залупе лешего вертели"
git push


Пробегаемся по всем файлам и каталогам, записываем текущие права в файл permissions.txt, коммитим, пушим.

Ну и скрипт для восстановления прав:

#!/bin/bash

if [ -f permissions.txt ]; then
while read perm file; do
if [ -f "$file" ]; then
chmod "$perm" "$file"
fi
done < permissions.txt
fi


Вот и вся наука. По желанию вешаешь это на Git Hook (post-checkout, post-merge) и автоматизируешь.

Как автоматизировать писал в этом посте, на примере можешь адаптировать.


А еще есть git-restore-mtime, для восстановления метаданных времени модификации файлов.

Такие дела, изучай…

tags: #linux #bash

🔔 ➡️
Аутентификация, авторизация. Навеяло.

Есть два вида запоминания паролей - хранение и связанная генерация. Это когда ты вводишь логин, а программа на основании его и модификатора (соли) вычисляет пароль.

Хранить лучше в программах типа keepass и аналогах. Сейчас затронем тему генерации.

Есть такая известная программа как pwgen. Просто чудесная. Столько всего умеет. В том числе и связывать логин и пароль. Проблема только в том, что при генерации командная строка может попасть в history.

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

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

Модификатор - это что-то типа парольной фразы при использовании ключа.

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

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

Модификатор может набираться с клавиатуры видимом и скрытом режимах. Все зависит от агрессивности коллектива.

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

Да, совсем забыл, параметр PWGEN_OPT - дополнительные опции генерации пароля НЕЛЬЗЯ менять после начала эксплуатации программы.

Программа pwgen должна быть установлена.

Для debian sudo apt install pwgen

declare -r PWGEN_OPT='-1 --symbols --ambiguous --capitalize'
declare -r PASSLEN=15
declare -r PASSNUM=5
declare -r KEYFILE=${0%/*}/'1.jpg'
declare -l HIDDENMOD=0
declare -r PWGEN=$(which pwgen)
declare LOGIN
declare MOD MOD1

if [[ ! "$PWGEN" ]];then
echo pwgen not installed
exit 1
fi
if [[ ! -s "$KEYFILE" ]];then
echo keyfile "$KEYFILE" not found
exit 2
fi
read -p "Input login:" LOGIN
if [[ "1y" =~ "$HIDDENMOD" ]];then
read -s -p "Input modificator:" MOD;echo
read -s -p "Confirm modificator:" MOD1;echo
if [[ "$MOD" != "$MOD1" ]];then
echo "Modificator not confirmed"
exit 3
fi
else
read -p "Input modificator:" MOD;echo
fi
echo pwgen options: $PWGEN_OPT
echo "Login: $LOGIN"
$PWGEN $PWGEN_OPT -H"${KEYFILE}#${LOGIN}${MOD}" $PASSLEN $PASSNUM


сохраняем в файл passgen.sh


chmod +x passgen.sh
./passgen.sh


По умолчанию ввод модификатора отображаемый. Для параноиков - установить HIDDENMOD=1 результат работы примерно такой:

Input login:tagd.ru
Input modificator:BashDaysTheBest

pwgen options: -1 --symbols --ambiguous --capitalize
Login: tagd.ru
aig3ohkie.Wah4X
AiguW~u7vohphae
eiJa7ahxei.die!
FaeNa=phah9voh3
Kih]ahca3Hie7ke


Если повторить ввод тютелька в тютельку пароли будут те же самые, что и требовалось.

➡️ Тыкни сюда, чтобы посмотреть описание работы программы.

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

Почитать:

help declare read
man pwgen
https://cheatsheets.zip


tags: #bash #linux © by Tagd Tagd

🔔 ➡️
Порой бывает необходимо прям из bash-скрипта добавить/удалить какое-нибудь задание в cron.

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

{ crontab -l; echo '# echo BashDays >/tmp/BashDays.txt' ; }| crontab -


ИЛИ:

{ crontab -l; echo '#*/10 * * * 5 echo BashDays >>/tmp/BashDays.txt' ; }| crontab -


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

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


#!/bin/bash

function ADD_CRON_TASK(){
local CRON_TASK=${1:-'#'}
local COMMENT=${2:-$(date +%s)}
COMMENT=${COMMENT// /_}
{ crontab -l 2>/dev/null; echo "$CRON_TASK #$COMMENT"; }| crontab -
}

function REMOVE_CRON_TASK(){
if [[ $# -eq 0 ]];then
echo No comment to remove cron task >&2
return
fi
COMMENT=${1// /_}
crontab -l 2>/dev/null|sed '/#'$COMMENT'$/d'|crontab -
}

CRON_TASK='# reboot'
COMMENT="My litle joke"
ADD_CRON_TASK "$CRON_TASK" "$COMMENT"

crontab -l|tail -3

echo '###################################################'
REMOVE_CRON_TASK "$COMMENT"

crontab -l |tail -3


Если запустить программу, увидите что-то типа:

# 
# m h dom mon dow command
# reboot #My_litle_joke
#############################
# For more information see the
#
# m h dom mon dow command


Видно, что программа добавила задание, вывела последние три строки crontab'a, потом разделитель.

Затем задание было удалено. Это видно по последним трем строкам.

В комментариях задания пробелы заменяются на "_" для упрощения работы c sed.

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

tags: #bash #linux © by Tagd Tagd

🔔 ➡️
У меня возникла проблема. Мне нужно переконфигурировать сеть на железном сервере, который находится в другом городе. И командировка туда займет пару дней.

Проблема усугубляется тем, что на сервере нет ipmi или аналогов, кроме того, в окружении нет админа. Поэтому конфигурировать сеть нужно по ssh.

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

Скрипт поможет в случае чего восстановить конфигурацию, в случае моего косяка.

Алгоритм работы следующий:

0. Есть работающий сервер.

1. При запуске создается резервная копия защищаемого каталога конфигурации /etc

2. В crontab добавляется задание, которое через определенное время ПОСЛЕ ПЕРЕЗАГРУЗКИ восстановит каталог из копии.

3. Если вышеуказанное задание определит, что в систему по ssh вошел указанный пользователь, восстановление отменяется и задание из crontab убирается.

4.Если пользователь не смог войти в систему - сначала создается вторая резервная копия защищаемого каталога (на тот случай, если сконфигурировали все правильно, но забыли/не смогли войти из-за проблем на вашей стороне, ну и для анализа), после этого производится восстановление резервной копии из пункта 1, убирается задание из crontab и производится перезагрузка сервера. Сервер приведен к пункту 0.


Пользоваться этой цацой просто:

1. ПЕРЕД НАЧАЛОМ КОНФИГУРИРОВАНИЯ запускаем скрипт, Он запрашивает имя пользователя и время ожидания до восстановления ПОСЛЕ ПЕРЕЗАГРУЗКИ.

2. Конфигурируем сервак.

3. Отправляем сервак в перезагрузку. (Да, не лучший вариант, но по сравнению с командировкой фигня).

4. Пытаемся войти по ssh.


По ssh мы войдем в любом случае. Или быстро (если сервак сконфигурировали правильно) или по окончании времени ожидания и перезагрузки.

Обращаю внимание - если пользователь смог войти в систему - Сервер считается работоспособным, задание из cron убирается.

И если вы решите что-то снова поконфигурировать - нужно снова запускать скрипт и один-два раза перезагружать сервак. Лучше составьте план действий.

Скрипт тестировался на debian путем удаления каталога /etc/network

Используйте скрипт под свою ответственность. Гарантировать, что он заработает на вашей системе я не могу. Для начала попробуйте защить какой-нибудь каталог в домашней директории.

Скрипт не сможет восстановить конфигурацию, если машина не грузится совсем (ну, например грохнули fstab)

На Alpine точно не работает по двум причинам:

1. crontab не понимает опцию (проблема решаемая через /etc/inid.d)

2. по умолчанию не работают команды last, who, w. Поэтому несколько проблематично определить наличие пользователя в системе.

➡️ Скрипт в комментариях к этому посту, сюда не влез сука!

tags: #bash #linux © by Tagd Tagd

🔔 ➡️
Принцесса для снятия стресса

Привет. Когда ты создаешь новый docker контейнер, то по умолчанию этому контейнеру назначается имя. Откуда оно берется?

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


<прилагательное><фамилия> и всегда написаны в нижнем регистре.

Например:

quirky_pare
agitated_turing
furious_heisenberg
romantic_curie


Окей. После некоторого ресёрча оказалось, что 90% докера написано на Golang. Вот это поворот!

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

➡️ Посмотреть можешь тут.

Мотаем этот код в самый низ и видим забавную хуйню:

if name == "boring_wozniak" /* Steve Wozniak is not boring */ {
goto begin
}


Этакая пасхалочка. Но зачем все эти имена?

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

Ну и на закуску адаптируем полученные знания на bash, генератор может выглядеть так:

#!/bin/bash

adjectives=(
"adoring" "agitated" "amazing" "angry" "awesome" "blissful" "bold"
"boring" "brave" "clever" "cool" "compassionate" "competent" "crazy"
"dazzling" "determined" "ecstatic" "elegant" "eloquent" "epic"
"exciting" "fervent" "focused" "furious" "gallant" "gifted" "goofy"
"gracious" "happy" "hardcore" "hopeful" "hungry" "infallible" "inspiring"
"jolly" "jovial" "keen" "kind" "laughing" "loving" "magical"
"mystifying" "naughty" "nervous" "nostalgic" "optimistic" "peaceful"
"practical" "priceless" "quirky" "recursing" "relaxed" "reverent"
"sad" "serene" "sharp" "silly" "sleepy" "stoic" "strange" "stupefied"
"suspicious" "tender" "thirsty" "trusting" "unruffled" "vibrant"
"vigilant" "wizardly" "wonderful" "youthful" "zealous" "zen"
)

scientists=(
"albattani" "allen" "archimedes" "ardinghelli" "babbage" "bashdays" "banach"
"banzai" "bardeen" "bartik" "bassi" "bell" "benz" "bhabha" "bhaskara"
"blackwell" "bohr" "booth" "borg" "bose" "boyd" "brahmagupta" "brattain"
"brown" "carson" "chandrasekhar" "colden" "cori" "cray" "curie"
"darwin" "davinci" "dijkstra" "dubinsky" "easley" "einstein"
"elion" "engelbart" "euclid" "euler" "fermat" "fermi" "feynman"
"franklin" "galileo" "galois" "goldberg" "goodall" "hawking"
"heisenberg" "hermann" "herschel" "hertz" "hodgkin" "hoover" "hopper"
"hypatia" "joliot" "kalam" "keller" "kowalevski" "lalande" "leavitt"
"lichterman" "liskov" "lovelace" "mayer" "mccarthy" "mcclintock"
"mclean" "mcnulty" "meitner" "mendel" "mendeleev" "minsky" "mirzakhani"
"moore" "napier" "newton" "nobel" "noether" "panini" "pare"
"pasteur" "payne" "perlman" "pike" "poincare" "ptolemy" "raman"
"ramanujan" "ride" "ritchie" "roentgen" "rosalind" "saha"
"sammet" "shockley" "sinoussi" "stallman" "stonebraker" "swanson"
"tesla" "thompson" "torvalds" "turing" "varahamihira" "visvesvaraya"
"wilbur" "wiles" "williams" "wilson" "wing" "wozniak" "wright"
"yonath"
)

generate_container_name() {
local adjective="${adjectives[RANDOM % ${#adjectives[@]}]}"
local scientist="${scientists[RANDOM % ${#scientists[@]}]}"
echo "${adjective}_${scientist}"
}

count=${1:-10}
for ((i=1; i<=count; i++)); do
generate_container_name
done


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

Теперь ты в праве заполнить массивы своими данными и генерировать уникальные имена, например: pizdatiy_huy или ohuennaya_pizda.

А потом использовать сразу в командах:

docker run --name $(bash script.sh) -d nginx


Есть еще такая штука:

alias dn='curl -s https://frightanic.com/goodies_content/docker-names.php'


Это выплюнет тебе подобное имя прям в консоли без скриптов и приседаний.

А еще есть библиотека на питоне — codenamize, которая проворачивает тоже самое.

Короче есть из чего выбрать. В общем пользуйся, прикручивай, изучай!

tags: #linux #bash #docker

🔔 ➡️
Bash генератор OTP для 2FA

Я давненько пользуюсь Authy. Это генератор OTP кодов для 2FA. Большим плюсом было — что этот генератор работал на десктопе.

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

TOTP (Time-based One-Time Password) — это алгоритм, используемый для генерации одноразовых паролей (OTP), которые действуют в течение ограниченного времени.


Короче в мою зону комфорта беспощадно насрали.

Каждое случившиеся гавно это повод изобрести велосипед что-то своё.

Пошло оно всё нахуй! Будем генерить коды Bash скриптом!

Для начала нам понадобится утилита zbar, она позволит из терминала распознать qr код, этот код выдают сервисы где включается 2FA.

Не парься, иксы не нужны, всё работает нативно в терминале.

sudo apt install zbar-tools


Далее пиздуем например в gitlab и включаем 2FA, в ответ оно тебе выплюнет QR код. Делаем скриншот QR и сохраняем.

Запускаем сканер:

zbarimg gitlab_qr.png


В ответ получаем нечто подобное:

QR-Code:otpauth://totp/gitlab.com:gitlab.com_hello%40devopsina.ru?secret=1234567890ABCDEFG&issuer=gitlab.com

scanned 1 barcode symbols from 1 images in 0.02 seconds


Замечательно. Из этой кишки нам нужен только secret=1234567890ABCDEFG.

Устанавливаем вторую утилиту:

sudo apt install oathtool


Накидываем bash скрипт:

#!/bin/bash

SECRET="1234567890ABCDEFG"
echo "Gitlab 2FA code: $(oathtool --totp -b "$SECRET")"


Запускаем и получаем желаемый OTP код, вводим его в Gitlab и охуеваем. Мы успешно прошли 2FA. Всё работает!

Базу расковыряли. Теперь нужно какое-то человеческое решение. Накидываем еще один bash скрипт:

#!/bin/bash

secrets=(
"GitLab:1234567890ABCDEFG"
"Google:1234567890ABCDEFG"
"Bashdays:1234567890ABCDEFG"
)

for i in "${!secrets[@]}"; do
IFS=":" read -r service secret <<< "${secrets[i]}"
echo "$((i + 1)). $service"
done

read -p "Введите номер сервиса: " choice

if [[ "$choice" -gt 0 && "$choice" -le "${#secrets[@]}" ]]; then
IFS=":" read -r selected_service selected_secret <<< "${secrets[choice - 1]}"
TOTPCODE=$(oathtool --totp -b "$selected_secret")
echo "TOTP код для $selected_service: $TOTPCODE"
else
echo "Неверный выбор."
fi

read -p ""


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

Что-то вроде:

secrets_file="secrets.txt"

while IFS= read -r line; do
secrets+=("$line")
done < "$secrets_file"


Сохраняем получившийся скрипт в qr.sh, делаем алиас если нужно и запускаем. В ответ получаем нумерованный список сервисов:

1. Gitlab
2. Google
3. Bashdays

Введите номер сервиса:


Вводим нужный номер сервиса и получаем актуальный OTP код.

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

Например, кидаешь боту QR картинку, он ее распознает, добавляет в sqlite базу этот сервис и генерит по запросу коды.

В общем тут уже полет фантазии, можно и кнопочки прикрутить и другие украшения. Было бы время и желание.

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

Вот такой хуйнёй переодически приходится заниматься.

Ладно, чо. Хорошего тебе дня, увидимся!

tags: #linux #bash #рабочиебудни

🔔 ➡️
Интересный вопрос сегодня залетел:

Если функции, вынесены в файл, подключенный через source, bash каждый раз его будет открывать/перечитывать при обращении к функции? Или как-то считает в память и все?


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

Предположим есть bash скрипт с функциями:

functions.sh

#!/bin/bash

bashdays_function() {
echo "Hello from BashDays"
}


И основной скрипт, который сорсит файл с функциями:

test_script.sh

#!/bin/bash

source ./functions.sh

bashdays_function
bashdays_function


Не забываем сделать: chmod +x test_script.sh

Запускаем test_script.sh под контролем strace:

strace -e trace=openat ./test_script.sh


И видим:


openat(AT_FDCWD, "./test_script.sh", O_RDONLY) = 3
openat(AT_FDCWD, "./functions.sh", O_RDONLY) = 3

Hello from BashDays
Hello from BashDays


То есть в контексте скрипта test_script.sh, файл с функциями был прочитан один раз.

При втором вызове функции всё считалось из памяти.

Если бы файл functions.sh читался каждый раз, то мы увидели бы несколько строчек openat(AT_FDCWD).

Грубо говоря при каждом запуске test_script.sh, подключенный файл через source будет прочитан один раз.

Вроде очевидно, но порой заставляет задуматься.

Изучай!

tags: #bash #debug #linux

🔔 ➡️
Задача та же. 8 ферзей.

Предыдущая часть здесь: Воспользуемся тонкостями языка. На шахматной доске 8 клеток (тут может круто подойти восьмеричная система.) Таким образом заменяем 8 вложенных циклов всего одним!!!, с преобразованием числа в восьмеричное.

Каждая цифра - отдельный столбец.

Значение - номер строки.

#!/bin/bash
#8 ферзей

awk 'BEGIN{
i=8^8
while(i--){
c=sprintf("%08o",i)
j=8
while(index(c,--j)){}
if(j<0){
j=8
while(j--){q[j]=substr(c,j+1,1)}
b=1
for(m=0;m<7 && b;m++){
for(j=m+1;j<8;j++){
a=q[m]-q[j];sub(/-/,"",a)
if(a==(j-m)){b=0;break}}}
if(b){print c}
}}}'
#---


while(index(c,--j)){} - чисел всего 8(0-7), если index=0 (нет цифры j в числе с из 8 цифр, значит каких-то цифр 2).

Обратите внимание тело цикла пустое! Если цикл прошел полностью - дублей нет. Вау.

Алгоритм пермутаций без повторений методом перебора. (проверка на горизонталь).

while(j--){q[j]=substr(c,j+1,1)} - преобразовали число c в массив цифр q[]

Дальше аналогично предыдущей программе, кроме sub(/-/,"",a) - еще один аналог модуля.

Просто отбрасываем "-". Как конструкция по скорости не знаю, не проверял.

Программа отработала примерно за 41с.

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

Кто там хотел изучать bash?

Слабо реализовать на чистом bash?

Интересно было бы и время выполнения сравнить.

tags: #bash © by Tagd Tagd

🔔 ➡️
Сегодня поговорим про подводные грабли.

Работая над скриптом решил немного оптимизировать код. А чего, зачем цикл, если строка всего одна.


echo "1"|
while read; do
echo $REPLY,2
done

# на

echo "1"| read
echo $REPLY,2


А не тут то было.

1,2
,2


Во втором случае read не читает значение. Точнее читает, иначе бы скрипт ожидал ввода с клавиатуры, но переменная REPLY пустая.

Перечитал help по read. Ничего не пойму. Пришлось обратиться к Роману.

Он пояснил, что во втором случае команда read запускается в отдельном subshell, считывает значение. Вот только передать переменную можно только в дочерние процессы, а в родительские нельзя.

Поэтому при завершении процесса read значение переменной REPLY теряется.

Пришлось читать, как работают конвейеры. Здесь писать не буду. Просто приведу ссылку на wiki: https://bit.ly/4fajufR

Оптимизировать можно, но так:

echo "1"| (read;echo $REPLY,2)
#или так
read < <(echo "1")
echo $REPLY,2
# если переменная одна то классика
a=$(echo 1)


Все, теперь я знаю, как работает pipeline. Надеюсь и Вы.

tags: #bash © by Tagd Tagd

🔔 ➡️
Привет, давай напишем очередной велосипед на Bash.

А напишем мы своё подобие команды cat, но с потоками.

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

Вот что у меня получилось:

#!/bin/bash

file=$1

threads=4

process_line() {
line="$1"
echo $line
}

export -f process_line

fifo="/tmp/fifo"
mkfifo "$fifo"
exec 3<>"$fifo"
rm "$fifo"

for ((i = 0; i < threads; i++)); do
echo >&3
done

while IFS= read -r line; do
read -u 3
{
process_line "$line"
echo >&3
} &
done < "$file"

wait
exec 3>&-


Не забываем chmod +x pcat

Запускать так: ./pcat.sh input.txt

➡️ Теперь разбираемся:

threads указываем количество потоков для чтения текстового файла.

process_line = функция, которая будет обрабатывать строку, у меня это простое echo, но можно накручивать любую логику.

export -f = экспортируем функцию, чтобы функция была доступна в subprocess (этот момент ранее в постах мы с тобой уже разбирали, ссылку не дам, по поиску найдешь если интересно).

fifo = задействуем FIFO для контроля потоков (чуть ниже объясню что это такое за хуйня).

mkfifo = создаём именованный канал /tmp/fifo для контроля количества одновременно запущенных потоков.

for ((i = 0; = заполняем каналы «семафора» чтобы ограничить потоки.

while IFS = читаем файл построчно и обрабатываем строки.

read -u 3 = ждем свободный слот «семафора», каждый поток блокируется до тех пор, пока не освободится место в «семафоре».

wait exec 3>&- = ждем завершение всех потоков

Что такое FIFO?

FIFO = «первым пришёл — первым ушёл».


Представляем ебейшую очередь в магазине:

1. Бабки встают в очередь
2. Первым обслуживают ту бабку, что пришла первой
3. Никто не может сказать — мне только спросить и пройти первым

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

Если ты складываешь книги в стопку и потом начинаешь снимать их, то ты будешь использовать LIFO (Last In, First Out).

Но если ты встал в очередь к кассе — это FIFO.

Думаю ты ты понял, тут все просто.

Единственное отличие от cat, у нас получилось, что строчки выводятся в порядке завершения потока. Какой поток быстрее завершился тот и папа.

строка4
строка1
строка2
строка3
строка5
строка6


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

Ну и домашка тебе: сделай так, чтобы строчки выводились по порядку (также, как и в файле), но использовалась многопоточность.

Пользуйтесь, чо!

tags: #bash #linux

🔔 ➡️
Добрый вечер, здрасти. За чо купил, за то и продаю.

На повестке — direnv.

Эт полезная хуёвина для расширения оболочки. И она достаточно известна среди населения в загнивающем западе.

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

Грубо говоря — позволяет не засирать .profile всяким дерьмом.

Работает так: перед каждым запуском оно проверяет наличие файла (.envrc или .env) в текущем или родительском каталоге.

Если такой файл существует, то он загружается в подоболочку bash. И все экспортируемые переменные становятся доступны для текущей оболочки.

Работает с большой четверкой: bash, zsh, fish, tcsh

Суть — используем переменные среды для конкретного проекта, не трогая при этом ~/.profile.


Что интересно, direnv это бинарник на golang, что уже подразумевает скорость и все плюхи связанные с этим.

Для убунты ставится так: apt install direnv

Как воткнуть это в другое место, подробно написано тут ну или спроси у медведя.


Подключаем так, добавляем в ~/.bashrc:

eval "$(direnv hook bash)"


Для других шелов смотрим тут.


Не забываем перечитать файл ~/.bashrc чтобы изменения вступили в силу.

Так, поставили, молодцы! Проверяем чо получилось:

cd /tmp
mkdir my-project

echo ${FOO-bashdays}


На экран выдалось: bashdays

Переменная среды FOO не вывелась, логично, дальше:

echo export FOO=foo > .envrc


Ёпта, ошибка direnv: error /tmp/my-project/.envrc is blocked. Run `direnv allow` to approve its content.

Сработала защита от дурака, которая запретила нам использовать .envrc. Хе, ща пофиксим!

direnv allow .


Во! Лепота!

direnv: loading /tmp/my-project/.envrc
direnv: export +FOO


Повторяем первую команду:

echo ${FOO-bashdays}


Хуяк и получаем: foo

Что и требовалось доказать, direnv отлично справилась с поставленной задачей.

Теперь выходим из проекта и смотрим:

cd ..

direnv: unloading

echo ${FOO-bashdays}


Ииии барабанная дробь, у нас вывелось — bashdays!

Какая же красота и любовь с первого взгляда!

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

➡️ Проект на гитхабе: https://github.com/direnv/direnv

⭐️ Star 12.6k

tags: #bash #linux

🔔 ➡️
Сегодня настроение ныть почему-то нет, поэтому научимся открывать файл на нужной строке в разных редакторах.

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

Вообще я встречал людей, которые так открывают файлы, когда видят нечто подобное:

Error in file "script.py" on line 42: NameError: name 'variable' is not defined


Хуяк и открыл script.py сразу на 42 строке.

Короче давай посмотрим как это сделать.

Создаем файл для теста:

( printf '%s\n' {A..Z}-$[++I] > /tmp/bashdays.txt )


Получаем такое содержимое:

A-1
B-2
C-3
D-4
E-5
F-6
G-7
H-8
I-9
...


Ну и теперь давай открывать это:

➡️ mcedit

mcedit +7 /tmp/bashdays.txt


➡️ less

less -w +11 /tmp/bashdays.txt


➡️ nano

nano +14 /tmp/bashdays.txt


➡️ vim

vim +4 /tmp/bashdays.txt


Вот такие пироги. А какие еще редакторы ты знаешь в которых можно открыть файл на определенной строке? Камон в комменты!

tags: #linux #tricks #bash

🔔 ➡️
👍1
Пули закончатся раньше чем ноги

Откапал сегодня в одном коммерческом интерпрайзе такую конструкцию:

while [ $(pgrep process_name)>'0' ]  
do
sleep 5
done


Смотрю я в неё и понять не могу, а нахуя?

Давай разберемся что тут не так.

«[» = это аналог или синоним встроенной команды «test». Если не задавать оператор, то на пустой строке вернется FALSE. Ну и логично что вернется TRUE если срока не пустая.

«while» — ежу понятно, условный цикл. Будем вертеть на хуй, пока команда заданная в условие возвращаешь истину. В нашем случае эта команда «test».

«pgrep» — ищем процесс с именем process_name. Эта штука выводит на стандартный вывод PID процесса. Если процесс не найден, код возврата отличный от нуля и нуль в противном случае.

«$(command)» — Command запускается в подоболочке. Результат выполнения, подставляется в текущую позицию.

«>» — Перенаправляет результат работы команды в файл (в нашем случае файл 0). Существующий файл, перезаписывается. Несуществующий, будет создан.

Понял в чем дело? Нет? Давай дальше…

Вопрос — нахуя здесь «test»? Цикл и так является условным и сам проверяет что вернула команда. Получается избыточность.

Команда test — Предназначена для проверки типа файла и сравнения чисел и строк. Возвращает код возврата 0 (истина) или 1 (ложь) в зависимости от вычисления выражения. Выражения могут быть как унарными, так и бинарными.


Такое обычно херачат, либо по привычке, либо когда нужно быстрое решение, а времени на ресерч нет. Работает да и хрен с ним.

Вся эта конструкция могла бы выглядеть так:

while pgrep process_name &> /dev/null  
do
sleep 5
done


Цикл будет шуршать, пока команда pgrep отрабатывает без ошибок и находит процесс.

Вот такая вот романтика, на хую два бантика… Изучай!

tags: #bash

🔔 ➡️
Набрёл сегодня на мануал по установке ack, а там такое:

curl https://beyondgrep.com/ack-2.28-single-file > ~/bin/ack && chmod 0755 !#:3


ack — инструмент для поиска текста в файлах, который часто используется разработчиками как более удобная и современная альтернатива `grep`. Он написан на Perl и ориентирован на работу с кодом.


В строке, в конце есть непонятности: !#:3

Опять какая-то магия, а магию мы с тобой любим.

Давай разберемся.

В данном случае !#:3 — это конструкция, связанная с историей команд в оболочке Bash/Zsh.

! = Используется для доступа к истории команд.

Например, !! повторит последнюю команду, а !123 выполнит команду с номером 123 в истории.

# = Ссылается на текущую команду, которая в данный момент пишется.

Вместо доступа к уже выполненной команде из истории, мы ссылаемся на текст текущей строки.

:3 = Указывает, что нужно взять третий аргумент в текущей строке.

Соответственно:

:1 — первый аргумент
:2 — второй аргумент
:3 — третий аргумент


Как это работает?

Сначала выполняется команда curl, которая скачает файл ack-2.28-single-file и сохранит его в ~/bin/ack.

После этого, команда chmod изменяет права доступа для файла.

А !#:3 подставляет третий аргумент текущей команды, то есть ~/bin/ack.

И по итогу команда превращается в chmod 0755 ~/bin/ack

 curl http://beyondgrep.com/ack-2.28-single-file > ~/bin/ack && chmod 0755 ~/bin/ack


Вот и вся магия. Упорото? Аще!

А зачем это нужно?

- удобно для автоматизации и избежания дублирования ввода
- снижает вероятность ошибок, если накосячил в путях

Короче эта хреновина делает команду более динамичной и минимизирует рукоблудие.

Штука прикольная, как говорится — век живи, век учись.

🔥 Ну и с пятницей друзья! Еще пару недель и каникулы.

tags: #bash

🔔 ➡️