Сначала короткая история о том как я увидел реальные возможности 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
Добавить комментарий