Курс
28. bash скрипты №2
В прошлый раз мы остановились на том, что сделали скрипт более универсальным – вместо двух определённых пользователей мы теперь можем создавать разных пользователей, используя переменные:
1
cat myscript
Copied!
Но сделав это мы перестали соответствовать начальным требованиям. Во-первых, мы говорили, что если группа it, её следует предварительно добавить в sudoers. Но в нашем скрипте всего одна переменная group и какое бы значение она не получила, она всё равно добавляется в sudoers. Если я буду добавлять пользователя с группой users, то она тоже попадёт в sudoers. Во-вторых, мы говорили, что оболочка bash только у пользователей группы it, у users должен быть nologin, значит сейчас скрипт подходит только для группы it. Для users мы можем создать отдельный скрипт, подходящий им. Но также можем это сделать в рамках одного скрипта.
Начнём с sudoers. Сформулируем задачу: если переменная group получит значение it, то нужно добавить такую-то строчку в sudoers. Если же переменная group получит любое другое значение, ничего в sudoers добавлять не надо. И так, у нас появилось слово «если» – это значит, что мы имеем дело с условиями. bash умеет работать с условиями, для этого у него есть команда if. Синтаксис команды выглядит так - if условие then команда, которую мы хотим выполнить, в случае если условие выполнилось, и в конце fi, чтобы показать, где у нас заканчивается команда if:
1
if условие
2
then команда
3
fi
Copied!
Если с командой понятно, то чем же является условие? Тоже командой – cp, ls, grep и всё в таком духе, то есть просто команда. Вы спросите – if ls, где тут условие? ls просто покажет список файлов, о каком условии идёт речь? На самом деле, if интересует не сама команда, а результат её выполнения, так называемый «код выхода» или «статус выхода». Обычно это числа от 0 до 255.
Мы упоминали статус выхода, когда говорили про зомби процессы – дочерний процесс при завершении должен передать родительскому свой статус выхода. Зачем? Это принятый в программировании способ родительским процессам узнать, как выполнился дочерний процесс, без необходимости копаться в выводе дочернего процесса. Для процесса, который выполнился без всяких проблем, обычно статус выхода – 0. Если же что-то пошло не так, то статус выхода другой, и по нему иногда легче понять причину проблемы.
В bash-е с помощью специальной переменной:
1
$?
Copied!
можно узнать статус выхода последней выполненной команды. Для примера возьмём команду ls. Просто выполнив команду и посмотрев значение переменной:
1
ls
2
echo $?
Copied!
мы увидим, что код выхода - 0. Теперь давайте выполним команду:
1
ls file
Copied!
Команда ругается, что такого файла нет. Посмотрим статус выхода ещё раз:
1
echo $?
Copied!
Как видите, теперь он 2. Есть определённые правила, в каких случаях какие числа пишутся, но сейчас нас интересует только 0.
Если процесс завершился с кодом 0, то всё окей. Все остальные коды считаются за ошибку. Именно так думает if. Давайте напишем маленький пример:
1
nano test
Copied!
1
if ls
2
then echo Success, because exit code is $?
3
fi
Copied!
1
cat test
2
./test
Copied!
Как видите, сработала команда ls, после чего команда echo.
Давайте направим вывод команды ls в никуда, чтобы не было лишней информации:
1
nano test
Copied!
1
if ls > /dev/null
2
then echo File exists
3
fi
Copied!
1
./test
Copied!
Теперь сделаем так, чтобы if получила не 0, а что-то другое:
1
nano test
Copied!
1
if ls file > /dev/null
2
then echo File exists
3
fi
Copied!
1
./test
Copied!
Как видите, теперь у нас условие выдаёт не 0, а значит команда после then не сработает.
Можно ещё if дополнить с помощью else, которая будет запускать команду, если в if условие не сработает. Выглядит это так:
1
if условие
2
then команда, если 0
3
else команда, если не 0
4
fi
Copied!
Например:
1
if ls file
2
then echo Success, because exit code is $?
3
else echo Failure, because exit status is $?
4
fi
Copied!
1
cat test
2
./test
Copied!
Как видите, сработала вторая команда.
Окей, по какому принципу работает if разобрались. Но этого недостаточно – нам нужно узнать, переменная group получила в качестве значение it или что-то другое. То есть нам нужна команда, которая сравнит значение переменной с каким-то текстом. Если всё окей, у неё статус выхода будет 0 и тогда if выполнит нужную команду. Такая команда есть и она, можно сказать, специально написана для if. Команда выглядит как открывающаяся квадратная скобка:
1
[
Copied!
а после выражения внутри ставится закрывающаяся квадратная скобка:
1
[ выражение ]
Copied!
Причём даже есть такая программа в /usr/bin:
1
ll /usr/bin/[
Copied!
но у bash обычно есть встроенная версия этой программы:
1
type [
Copied!
а внешняя программа может понадобится для других оболочек. Есть такая же программа с названием test:
1
type test
Copied!
и даже более продвинутая версия с двумя скобками:
1
type [[
Copied!
но она есть на bash и паре других оболочек, а не на всех. В общем, если хотим, чтобы нашими скриптами можно было пользоваться не только на bash, будем обходиться без двойных скобок.
С помощью квадратных скобок можно сравнивать много чего – строки, числа, наличие файлов, директорий, их права и многое другое. Программа сравнивает, выдаёт значение 0 или 1, а дальше if действует так, как мы обсуждали. По ссылке показан синтаксис для различных сравнений, а пока посмотрим нашу ситуацию.
Нам нужно сравнить переменную group и текст it:
1
[ $group = it ]
Copied!
Для примера, дадим переменной значение users, сравним и посмотрим код выхода:
1
group=users
2
[ $group = it ]
3
echo $?
Copied!
Как видите = 1, то есть неправильно. Теперь дадим переменной значение it, сравним и посмотрим код выхода:
1
group=it
2
[ $group = it ]
3
echo $?
Copied!
0 – значит всё правильно.
Кстати, важное замечание, если в переменной есть пробелы, то сравнение работать не будет:
1
group="a b c"
2
[ $group = it ]
Copied!
так как bash превратит нашу переменную в её значение и в выражении получится много параметров, типа:
1
[ a b c = it ]
Copied!
а это не подходит под синтаксис. Поэтому лучше переменные брать в кавычки:
1
[ "$group" = it ]
Copied!
Хорошо, с выражением разобрались, вернёмся к нашему скрипту и попробуем его добавить.
Вспомним наше условие - если переменная group получит значение it, то нужно добавить такую-то строчку в sudoers:
1
nano myscript
Copied!
Находим нашу строчку с командой echo и окружаем её условием:
1
if [ "$group" = it ]
2
then
3
...
4
fi
Copied!
Тут else нам не нужен, а команды после then можно чуток подвинуть направо с помощью пробелов, чтобы было проще для глаз.
Теперь вспомним второе условие - оболочка bash только у пользователей группы it, у users должен быть nologin. Тоже довольно просто – наверху создаём переменную shell, чтобы легче было её поменять при надобности и даём ей значение:
1
shell=/sbin/nologin
Copied!
А в if добавляем:
1
shell=/bin/bash
Copied!
И не забываем в конце указать эту переменную в качестве оболочки:
1
-s $shell
Copied!
Если у нас группа будет it, то значение переменной в if поменяется на bash, а если не it, то условие не выполнится и переменная shell останется nologin.
Попробуем запустить скрипт 2 раза – один раз для пользователя с группой it, а потом для пользователя с группой users:
1
sudo ./myscript
2
tail /etc/passwd
Copied!
Всё сработало.
Но если посмотреть sudoers:
1
sudo tail -5 /etc/sudoers
Copied!
можно заметить, что строчка it повторяется много раз, хотя одной строчки вполне достаточно.
Давайте решим и эту проблему. Сформулируем задачу – если мы добавляем пользователя с группой it и если в sudoers нет нужной строчки – то её нужно создать. То есть, если выполнилось первое условие, нужно проверить второе условие и если оно тоже выполнилось – то только тогда добавлять строчку.
Первое условие у нас уже прописано. После его выполнения, то есть после then пишем ещё один if. Теперь нужно написать условие – если такой-то строчки нет в sudoers. Поиском строчек занимается команда grep. Посмотрим, что он нам выдаёт, если мы попытаемся найти строчку. Необязательно искать всю строчку, если есть что-то про группу it, то этого достаточно:
1
sudo grep '%it' /etc/sudoers
Copied!
И посмотрим код выхода:
1
echo $?
Copied!
Ноль. Посмотрим, что будет, если он не найдёт строчку – напишем в grep какую-то несуществующую группу, чтобы он ничего не нашёл:
1
sudo grep '%itаа' /etc/sudoers
Copied!
и посмотрим ещё раз:
1
echo $?
Copied!
Один.
И так, если строчек нет, то grep выдаёт 1, если есть – 0. Мне же нужно, чтобы запись создавалась, если строчек нет, то есть grep должен выдать 1, а if должен получить 0. Чтобы вот так вот перевернуть полученное значение, нужно после if поставить восклицательный знак. Давайте проверим на том же ls, чтобы было проще. И так, если ls не выполнился, то команда после then не должна сработать:
1
if ls file
2
then echo File exists
3
fi
Copied!
Как видите, ls выдал ошибку и условие провалилось.
Но если мы после if поставим восклицательный знак:
1
if ! ls file
2
then echo File exits
3
fi
Copied!
то ls, опять же, не сработал, но if перевернул значение, условие выполнилось и команда сработала.
Попробуем тоже самое с нашим скриптом:
1
if ! grep "%$group" /etc/sudoers"
2
...
Copied!
И так, прочтём – если переменная group равна it запускается второе условие – если grep не нашёл упоминание группы it в sudoers, то следует добавить в sudoers эту группу.
Давайте проверим. Для начала удалим все упоминания группы it в sudoers:
1
sudo visudo
Copied!
Потом запустим наш скрипт и создадим пользователя с группой it:
1
sudo ./myscript
Copied!
Теперь добавим ещё одного пользователя с группой it:
1
sudo ./myscript
Copied!
Проверяем:
1
sudo tail -3 /etc/sudoers
Copied!
всё также одна строчка. Условие работает.
Мы с вами разобрали условие if, которое добавляет вашим скриптам логики, чтобы они могли проверять какие-то условия и по результатам менять какие-то переменные или выполнять дополнительные команды. Также разобрались, для чего нужны коды выхода и использовали их для команды if. Не забудем и про программу квадратных скобок, которая позволяет нам сравнивать переменные, строки, числа и проверять файлы. Мы ещё разберём, как в команде if проверять другие условия с помощью elif и много чего другого.
Last modified 27d ago
Export as PDF
Copy link