ITСooky

IT-рецепты съедобные и не очень!

Как Linkedin помог освоить Bash и почему он не Expect!

дата 29.07.2019

Сначала короткая история о том как я увидел реальные возможности Bash и захотел переписать старый скрипт на него. Все началось с Linkedin (запрещенная в России организация), для тех кто не знает — это социальная сеть для компаний и работников, там реально есть вакансии НАСТОЯЩИХ(с зарплатой около 100 kRUR для IT Specialist) международных компаний, разумеется этих вакансий нет на hh.ru потому что западный HR не знает про него, а у некоторых компаний нет HR и начальники сами ищют сотрудников, потому что кто, как не они сами, понимают какой айтишник им нужен.

Конечно нужен английский, конечно мы(айтишники) на английском больше читаем-пишем, чем говорим. Вот чтобы услышать технический английский прошел пару курсов которые подкатывает Linkedin. Они как бы «бесплатные», все что нужно — заполнить свой профиль, сходить в inLearning там отметить интересные курсы, и соцсеть начнет предлагать бесплатные, близкие к моим интересам, теперь при обновление главной странице сверху будет предлагаться новый курс — бесплатно (Аааааы ограничение 8-10 курсов в месяц, так что «чуз вайзли»). Если перейти на такое предложение то сразу получишь 24 часа на прохождение курса. Есть конечно и подлость в «бесплатности» и полезности таких курсов. Предлагают «бесплатно» один месяц доступа к курсам — для этого надо заплатить($%#!) за один месяц и не забыть отменить оплату через месяц — это бесплатный месяц. Если успели пройти курс за 24 часа, его можно добавить в свои обучения-сертификаты, и сразу же добавятся ключевые умения — плохо что доступ к курсу сразу закроется(только ненужные части остаются октрыты, как введение), а после него не остается никакого документа-резюме с командами действиями (если сам не конспектировал) это подло это так продвигается годовая подписка, чтобы можно было в любой момент заглянуть в видео и посмотреть нужные комманды-действия. Все это делает эти курсы немного ущербными, но английский они помогают подтянуть, и люди кто не знаком с этими линкедин курсами очень даже их серьезно воспринимают. И вот еще, если вам дали 24 часа на курс бесплатно, то задания к нему скачать все равно можно только за платно, но я их не скачивал, сразу смотрел решение и так понятно. Английский язык тоже иногдаа ыыы вот в курсе Windows 10: Troubleshooting for IT Support у товарища Andrew Bettany какие то проблемы с выговариванием буквы а в словах plug(у него плюг), corrupted(у него корруптед), да ему даже run удавалось как то через у произносить рыыыыээун!

Но мне эти курсы все равно очень нравятся. В том же Windows 10: Troubleshooting for IT Support было показано много нужного, что никогда реально не использовалось 🙂 И вот я посмотрел Learning Bash Scripting ведущий Scott Simpson — оказалось, крутая штука, для красивой визуализации, для автоматизации пальчиковой работы — надо брать!

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

Например Скотт приводит пример:

#!/bin/bash
[[ 20 -gt 100 ]]
echo $?

Исполняет, говорит: «нам возвращается 1 значит TRUE, вот так в bash можно вставлять математику». А вот нифига
echo $? — выводит Return Code или Exit Code Number статус завершения предыдущего действия.
А предыдущее действие спрашивает 20 больше 100 — FALSE, тоесть должно бы быть 0, а тут 1 что значит Catchall for general errors тоесть вообще не сработали эти кавычухи!

Нус начнем. Я беру FreeBSD 11.2 …я вообще то думал что у меня bash уже стоял, ан нет!

Я так думал потому что, конечно у меня уже были скрипты, и они начинались со строчки

#!/bin/bash

но запускал я их коммандой
sh ./test.sh
Это вообще две большие разницы, и в курсе рассказывается история создания баша… запутанная история…

И так ставим bash на FreeBSD по старинке
whereis bash
cd /usr/ports/shells/bash
make
make install

Вот еще в чем полезность таких курсов, какую то маленькую но нужную фигнюшку всегда замечаешь.
Как я раньше возвращался в свою директорию — пальцами набивал весь путь
но есть же командам сразу вернутся в свою юзерскую дирректорию даже не зная какая она
cd ~
или сразу в какую-то дирректорию в ней
cd ~/work

Смотрим версию нашего bash
bash --version
ответ

GNU bash, version 4.4.23(0)-release (amd64-portbld-freebsd11.2)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
You have new mail in /var/mail/root

Беру вот этот код из статьи 2011 года, 11-го мама мия! Статья 11-го года! Скрипт на языке expect, уже давно умерший, интересный только цифровым археологам, но тогда в 11-ом у меня с bash с наскока не получилось, а с expect почему-то в друг получилось!!!

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

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

Вот, создаем одной командой куча файлов чтобы в имяни была цифра от 1 до 100 с шагом 25, и чтобы пустые места были заполнены ноликами
touch file{01..100..25}
получилось, цифры можно заменить буквами алфавита, имяна написать несколько
touch {fame,fork,frog}{01..100..25}
удалим сразу кучу оставим только file*
rm {fame*,fork*,frog*}
Уже начинаешь чувствовать автоматизацию как пальцы отдыхают!
Кстати Bash пока тут не причем, и не хочу никого шокировать он всегда будет не причем, за исключением несколько команд — в нем просто будут обычные консольные команды, собранные в одном файле с добавлением чуть-чуть логики!

Посчитаем сколько у нас файлов с названием file
ls file* | wc -l
Посчитаем сколько у нас файлов название которых есть о
ls *o* | wc -l
И не важно большая это о или маленькая О, в регулярные выражения не будут пока влезать, для их познания одной жизни не хватит
ls {*o*,*O*} | wc -l
удалим сразу кучу оставим только file*
rm {fame*,fork*,frog*}

Теперь давайте выведем список файлов с номером строки, вообще то для этого есть готовая команда
ls file* | nl

     1	file001
     2	file026
     3	file051
     4	file076

Но что если мы её не знаем, а я правда не знал nl что это вообще такое! Есть еще куча способ как это сделать!

Вот теперь включаем Bash. У меня от него расширилось понимание циклов, хотя раньше я тоже это замечал, но оформилось вот только сейчас. Цикл это делай что-то пока i не будет равно 10, а внутри где-то i равно i плюс один. А что еси вместо i комманда, а вместо 10… — а нет 10, все что выводит команда, пока не кончит выводить!

#!/bin/bash
count=1
for i in $( ls file* ); do
echo "line number" $count":" $i
let count++
done

Во-первых внимание: пробелы тут не просто так, пробелы имеют значение, через них много смешных непонятных ошибык, попробуйте напимсать count = 1 казалось бы что такого

Здесь у нас в качества цикла и переменной в этом же цикле, вывод команды ls file*, до конца этого вывода, даже не знаю когда он закончится и это здорово.

Дальше нам нужен номер строки, делаем переменную count, и добавляем к ней один вот так ++ это всего лишь +1 это нужно знать, это такая основа основ что нагуглить это невозможно!

и вот что на выходе

line number 1: file001
line number 2: file026
line number 3: file051
line number 4: file076

Если поднапрячься, то можно все это засунуть, вместе с логикой, в консольную команду, но зачем напрягаться есть же Bash — где все понятно сверху в низ, а не в одну линию!

Теперь ближе к тому что надо — переписать скрипт на Bash. Мы вставляем флешку в FreeBSD, она её сама не монтирует и это правильно, и ищем там нужный файл ключа к зашифрованному диском, это просто файл с известным именем. А что если Флешки вставлено две, надо поискать на обоих, а что если одна уже перемонтирована… это не будет универсальный скрипт, в сервере вообще не должно быть никаких флешек, программирую под себя (упс! хорошо сказано)… наверное выдаст ошибку на эту команду и пойдет дальше — не страшно!

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

Опять гуглю, а в курсе это было, а не посмотреть, maldito inlearning!

Вот скрипт который проверяет наличие файлов в диретории

#!/bin/bash
exec 2>/dev/null
my_array=(file001 file011 file026)
for i in "${my_array[@]}"; do
result=`ls $i`
if [ -n "$result" ]; then
echo $i": OK"
else
echo $i": none"
fi
done

exec 2>/dev/null — это строка говорит что нам не надо валить сообщения об ошибках в консоль(всех даже bash), но иногда все равно валятся вывод успешно завершенных команд… поэтому его прячу в переменную result
my_array=(file001 file011 file026) — это массив где имяна файлов которые мы ищем
Далее у нас цикл переменная в котором этот массив. Далее lsим, if тут TRUE если есть ответ на попутку элеснуть файл, и false если попытка не удалась, тоесть пусто в переменной!
На выход получаем

file001: OK
file011: none
file026: OK

Хорошо!

Теперь скрипт — который ищет на флешке два файла. Не учитываем что файл может быть один, что файлы могут быть на разных флешках. Единственно учитываем что флешек может быть вставленно много. Папка /mnt/usb у нас уже должна быть. Ищем флешку форматированную в FAT16 или FAT32.

Вот такой скрипт, в конце говорит найдено 2 файла на такой то флешке

#!/bin/bash
exec 2>/dev/null
fkeys=0
usbname=none
my_array=(file1 file2)
for a in $( ls /dev/da*s* ); do
mount_msdosfs $a /mnt/usb
for i in "${my_array[@]}"; do
result=`ls /mnt/usb/$i`
if [ -n "$result" ]; then
let fkeys++
usbname=$a
fi
done
umount $a
done
if [ -n "$fkeys" ]; then
echo "Found "$fkeys "in usb drive "$usbname
else
echo "Nothing found"
fi

Идем дальше. Теперь нужно инциализировать зашифрованный диск. Делается это коммандой geom attach и вот она странная. В команде можно передать ключфайл но нельзя передать словоключ, а bash не умеет как expect взаимодействовать со всем что на экране висит ждет ответа. Вспомнил теперь почему я expect то в 2011 заюзал, когда пытаешь сделать все из под виндоса одной иконкой, то там, учитывая способности bash наступает момент когда надо напрямую в комманду вводить пароль, а через putty windows окно это не срабатывала, а expect это было не страшно….

…как работал Expect — запустим команду > дождемся когда на эркане появится что-то > о пошлем на экран что хотим нажмем энтер как будто это человек сделал
…как работает Bash — запустим команду > она закончится выдаст какое-то сообщение > покапаемся в этом сообщение, запустим другую команду

Интерактива нет!

И так на ввод пароля будет 5 попыток, в какой-то момент bash встанет и будет взаимодейтсвие только с geom attach что там как прошло, можно будет узнать только проверкой, с этой команды ответ не получить (есть какие то pipe но это вроде не то), проверим сами появился ли нужный раздел!

#!/bin/bash
exec 2>/dev/null
ptry=0
while [ $ptry -le 4 ]; do
geli attach -k /mnt/usb/file  /dev/ada5
if ls /dev/ada5.eli; then
break
else
let ptry++
fi
done
echo "Ok"

Еще нуден код который будет проверять как завершилась комманда, если была ошибка то какая. В мануале для комманды mount описаны RETURN CODES — 0 все ок, если цифорки чтото случилось, и вот эти цифорки они не очем, они могут быть одни и теже у разных ошибок.

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

#!/bin/bash
#exec 2>/dev/null
mount /dev/ada0 /mnt/usb
error_num=`echo $?`
if [ $error_num -gt 0 ]; then
echo $error_num
fi
mount /dev/ada32 /mnt/usb
error_num=`echo $?`
if [ $error_num -gt 0 ]; then
echo $error_num
fi
domount=`mount /dev/ada0 /mnt/usb 2>&1`
echo "MESSAGE: "$domount
if [[ $domount = *"Operation not permitted"* ]]; then
echo "YES"
fi

Это нужно, потому что входе использования крипто разделов понял, что если была потеря питания, они не примонтируются выйдет ошибка Operation not permitted и надо запускать fsck и монтировать только для чтения.
И пока это не словишь, не понять что это бывает… печально однако! В Expect все проше, он в любой момент на экране ловит нужные слова и что-то делает. В bash надо ухитяртся, писать в переменную комманду с какой-то фигней на конце, иначе не запишет ошибку.

Ну и самое последнее, нужен красивый вывод информации о подключенных дисках
Делаем скрипт, для наглядности тут массив с заданными разделами для выводы информации, одного раздела нет — выведет ошибку

#!/bin/bash
my_array=(/ada0s1a /ada0s2a /da0s1)
for i in "${my_array[@]}"; do
my_array2=( `df -h | grep $i` )
if [ -n "${my_array2[0]}" ]; then
printf "\e[44m  ON-LINE: ${my_array2[0]} \e[0m \n"
printf "%10s\t%s\n" "Size:" "${my_array2[1]}" "Avail:" "${my_array2[3]}" "Mount on:" "${my_array2[5]}"
else
printf "\e[31m  --ERROR NONE $i \e[0m \n"
fi
done

Красивая картинка получается вот

И вот весь код целиком, полностью на Bash

#!/bin/bash
exec 2>/dev/null
fkeys=0
usbname=none
my_array=(file1 file2)
my_array2=(ada1 ada2)
my_array3=(/usr/1 /usr/2)
my_an=0
for a in $( ls /dev/da*s* ); do
mount_msdosfs $a /mnt/usb
 
for i in "${my_array[@]}"; do
result=`ls /mnt/usb/$i`
if [ -n "$result" ]; then
let fkeys++
usbname=$a
fi
done
umount $a
done
if [ -n "$fkeys" ]; then
echo "Found "$fkeys "in usb drive "$usbname
mount_msdosfs $usbname /mnt/usb
else
echo "Nothing founded"
break
fi
 
for i in "${my_array[@]}"; do
ptry=0
while [ $ptry -le 2 ]; do
echo "Lets activate encrypted device "${my_array2[$my_an]}"?"
geli attach -k /mnt/usb/$i  /dev/${my_array2[$my_an]}
if ls /dev/${my_array2[$my_an]}.eli; then
echo ${my_array2[$my_an]}".eli OK"
break
else
let ptry++
fi
done
let my_an++
done
umount $usbname
my_an=0
for i in "${my_array2[@]}"; do
mount /dev/$i.eli ${my_array3[$my_an]}
if [[ $? == 1 ]]; then
echo "Ups some error on mount, "$i".eli must be repeard"
/sbin/fsck -y -t ufs /dev/$i.eli&&mount  -o ro /dev/$i.eli ${my_array3[$my_an]}
fi
my_array4=( $(df -h|grep $i) )
if [ -n "${my_array4[0]}" ]; then
printf "\e[44m  ON-LINE: ${my_array4[0]} \e[0m \n"
printf "%10s\t%s\n" "Size:" "${my_array4[1]}" "Avail:" "${my_array4[3]}" "Mount on:" "${my_array4[5]}"
else
printf "\e[31m  --ERROR NONE $i \e[0m \n"
fi
let my_an++
done

В начале в массивах с именами my_array записаны имяна файлов ключей, разделы, и папки куда монтировать. Первым делом скрипт монтирует флешки и ишит имяна файлов ключей. Потом инициализирует крипто разделы, здесь запрашивается пароль. Потом пытается смонтировать эти разделы, здесь если есть ошибка с текстом Operation not permitted то запускает проверку. Потом проверяет появились ли разделы среди примонтированных и выводит синим цевтом статус если ок.

UPD: Переимяновываем кучу файлов по шаблону
Жизненная ситуация, например вы скачали кучу нужных файлов, с того же линкедин, скачивание видео никак не препятствуется ссылки на него есть в коде в явном виде надо только поискать mp4. Но имя файлу присваевается с какой-то лабудой… или лабадой… вот Bash скрипт который переимянует все пачкой

#!/bin/bash
for i in $( ls *.mp4?* ); do
a=`echo $i | awk -F? '{print $1}'`
cp $i "./"$a
done

1. Тут цикл из вывода комманды ls всех файлов содержащих в названии .mp4?
2. Далее awk говорит в названии файла делителем является ? и возьмем его первую колонку, это четко отделяет то имя которое нам падо
3. В комманду копировать всталяем исходное имя из цикла, и переменную созданную из вывода комнады awk
4. Могбы сюда вставить и удаление оригинала но побоялся

Запускаем
bash ./cutname.sh
Сработало, удаляю старые
rm *.mp4?*
Кстати могбы вместо копирования cp сразу использовать mv

UPD: Работаем с папками с пробелом рекурсивно
Мне это показалось простым делом и в консоле это просто да ставишь путь в «» и все ок, а в Bash нет!

По порядку, нам надо получить абсолютный путь к тем же файлам, делаем это коммандой
find $PWD ./ -type f -name *.mp4?*

И тут я залип на пару часов, оказалось что если цикл встречает пробел он тоже это считает разделителем, а не только конец линии, поэтому надо указывать через IFS что конец только конец линии

#!/bin/bash
IFS=$'\n'
for i in $( find  $PWD ./ -type f -name *.mp4?* ); do
a=`echo $i | awk -F? '{print $1}'`
cp "$i" "$a"
done

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *