BASH. Условия, циклы, функции.
Основы работы с Linux. -> BASH. Условия, циклы, функции.
BASH. Условия, циклы, функции.
Условия
Базовая форма.
if [[ "$some_variable" == "good input" ]]; then
echo "You got the right input."
elif [[ "$some_variable" == "ok input" ]]; then
echo "Close enough"
else
echo "No way Jose."
fi
fi придает предложению немного экзотический вид
Если (if) что-то является истиной, тогда (then) выполни вот это. В противном случае проверяй другие условия по порядку и делай то же самое.
Если ни одно из условий не сработало, выполни последнее указание.
Можно отбросить одну или все ветки elif, а также ветку else, если в них нет нужды!
Булевы операторы.
! - нет
&& - и
|| - или
Например.
if [[ "$name" == "Ryan" ]] && ! [[ "$time" -lt 2000 ]]; then
Иногда квадратные скобки могут быть одинарными
if [ "$age" -gt 30 ]; then
А иногда и круглыми.
if (( age > 30 )); then
А иногда их вообще нет
if is_upper "$1"; then
Есть магические слова, которые стоят за работой if в Bash: коды выхода.
Содержимое сразу после if может быть вообще любой командой, если она дает код выхода.
Если команда возвращает код выхода 0 (в Bash это код успешно выполненной операции), тогда запускается код внутри ветки then.
Существует специальная команда – [ (левая квадратная скобка). Она является синонимом команды test, и является встроенной командой (т.е. более эффективной, в смысле производительности). Эта команда воспринимает свои аргументы как выражение сравнения или как файловую проверку и возвращает код завершения в соответствии с результатами проверки (0 – истина, 1 – ложь).
Начиная с версии 2.02, Bash предоставляет в распоряжение программиста конструкцию [[ … ]] расширенный вариант команды test, которая выполняет сравнение способом более знакомым программистам, пишущим на других языках программирования. Двойные квадратные скобки работают в целом так же, как и одинарные квадратные скобки, но имеют дополнительные возможности вроде лучшей поддержки регулярных выражений.
Круглые скобки (( … )) и предложение let … так же возвращают код 0, если результатом арифметического выражения является ненулевое значение.
(( Двойные круглые скобки )) это конструкция, позволяющая осуществлять арифметические вычисления внутри Bash. Ими можно быстро инкрементировать счетчики и обновлять числовые переменные.
count=0
(( count++ ))
Если результат в скобках равен нулю, возвращается код выхода 1 (по сути, нулевой результат это «ложь»). Любой другой результат считается истиной, при нем код выхода будет 0.
Знаки «больше» и «меньше» внутри круглых скобок работают прекрасно когда как в квадратных путаются с переправлением вывода.
Примеры.
Пытаемся изменить текущий каталог.
if cd /tmp
then
echo "Success!!!"
ls -l
fi
Работаем с конвеером команд.
if ls | grep pdf
then
....
fi
При конвеере только последняя команда определяет будет ли выбрана ветвь.
if ls | grep pdf | wc
Проверяем на существование файл.
if [[ -e $FILENAME ]]
then
...
fi
Список дополнительных тестов.
Операторы проверки файлов
-d - существует ли каталог
-e - существует ли файл
-r - существует ли файл и доступел ли он для чтения
-w - существует ли файл и доступел ли он для записи
-x - существует ли файл и является ли он исполняемым
Числовые тестовые операторы.
-eq - равно
-gt - больше
-lt - меньше
Двойные скобки
if (( VAL < 12 ))
Внутри скобок не нужен оператор $ для получения значения за исключением позиционных параметров $1 $2 и т.д. (чтобы не путать их с константами 1 2 и т.д.)
В двойных скобках любое ненулевое значение является истинным и только ноль - ложным.
Анализ результата выполнения предыдущей команды
if (( $? )) ; then ... ; fi
Разделение команд.
Команды в BASH можно писать в одну строку разделяя ; && или ||
Примеры
cd $DIR; ls - команды выполнятся одна за одной
cd $DIR && ls - команда ls выполнится в случае успешной первой
cd $DIR || ls - команда ls выполнится в случае СБОЯ первой
Можно использовать тесты [[ ]] не только в конструкциях if
[[ -d $DIR ]] && ls "$DIR"
Циклы
Цикл с оператором while по структуре похож на if в том смысле, что для принятия решения он может принимать одну команду или конвейер команд, выдающих true или false .
В нем также можно использовать квадратные или круглые скобки, как в предыдущих примерах.
В некоторых языках программирования фигурные скобки (символы {} ) предназначены для группировки операторов, которые находятся в теле цикла while .
В таких языках программирования, как Python, тело цикла и его операторы определяются отступом.
В bash операторы сгруппированы между двумя ключевыми словами: do и done .
Вот простой цикл while :
i=0
while (( i < 1000 ))
do
echo $i
let i++
done
let - это встренная функция BASH которая выполняет арифметические операции.
Аналогично можно воспользоваться круглыми скобками.
(( i++ ))
Синтакс
let arg [arg ...]
При этом let выполняет арифметические операции со всеми переданными аргументами слева на право.
Список некоторых арифметических операторов.
** var++ var– ++var –var ** - сложение, вычитание, причем с пред и пост интерпретацией.
! - логическое отрицание
** *, /, % ** - умножение, деление, модуль
<<, >> - побитовое смещение
Вот более сложный цикл while , который выполняет команды как часть своего условия:
while ls | grep -q pdf
do
echo -n 'there is a file with pdf in its name here:'
pwd
cd ..
done
grep -q - при поиске молчаливо игнорировать ошибки
echo -n - не выводить символы переноса строк
Цикл for также доступен в bash, причем в трех вариантах.
Вариант 1.
for ((i=0; i < 100; i++))
do
echo $i
done
Вариант 2.
for ARG
do
echo here is an argument: $ARG
done
ARG - можно заменить любой переменной.
Результат вывода.
$ ./args.sh bash is fun
here is an argument: bash
here is an argument: is
here is an argument: fun
Вариант 3 с явным заданием аргументов.
for VAL in 20 3 dog peach 7 vanilla
do
echo $VAL
done
Значения, указанные в цикле for , также можно генерировать, вызывая другие программы или используя другие функции оболочки:
for VAL in $(ls | grep pdf) {0..5}
do
echo $VAL
done
$(ls | grep pdf) - вызывает комманду в подоболочке.
{0..5} - определение диапазона, можно с шагом {1..104..2}
Здесь переменная VAL , в свою очередь, будет принимать значение для каждого файла, который командой ls передается в grep и содержит буквы pdf в своем имени (например, doc.pdf или notapdfile.txt ), а затем переменная VAL примет значение каждого числа от 0 до 5.
Функции.
Синтаксис функции в bash следующий:
function myfun ()
{
# это тело функции
}
Не все эти компоненты обязательны. Вы можете указать или function , или () .
В определении функции параметры не объявляются. Какие бы аргументы и их количество при вызове функции ни приводились, они передаются этой функции.
Функция вызывается (активизируется) так же, как и любая команда в командной оболочке. Определив myfun как функцию, вы можете вызвать ее следующим образом:
myfun 2 arb "14 years"
Внутри определения функции аргументы упоминаются так же, как параметры сценария оболочки, то есть как $1 , $2 и т. д.
Это означает, что аргументы «скрывают» параметры, первоначально переданные в сценарий.
Если вы хотите получить доступ к первому параметру скрипта, то перед вызовом функции нужно сохранить $1 в переменной (или передать его в качестве параметра функции).
Если указанная внутри функции команда не объявлена как local , переменные в видимой области являются глобальными. Так же как и переменные тоже глобальные если явно не определены как локальные.
Вызов функции в подоболочке.
RETVAL = $ (myfunc args)
Но тут нужно помнить что изменения всех глобальных переменных будут актуальны только для подоболочки а не в текущем экземпляре.
$# - выдает количество аргументов, переданных функции
Возвращаемое значение.
Функции, как и команды, должны возвращать статус: 0 , если все идет хорошо, и значение, отличное от нуля, если произошла ошибка.
Шаблон соответствия в BASH. 3 подстановочных символа * ? [].
Шаблоны не являются регулярными выражениями , не путайте их. К примеру точка тут обычный символ.
Шаблоны сопоставляются с файлами в файловой системе.
Когда в командной строке нужно перечислить много файлов, не обязательно вводить имя каждого. Bash обеспечивает сопоставление с шаблоном (иногда называемое подстановкой знаков (wildcarding)), чтобы вы могли указать набор файловс шаблоном.
Самый простой подстановочный знак — символ звездочки ( * ), который будет соответствовать любому количеству любых символов.
Например
*.txt - соответствует любому файлу с расширением txt.
/usr/bin/g* - соответствует всем файлам в /usr/bin , которые начинаются с буквы g
Если при сопоставлении необходимо учитывать *, то его необходимо экранировать знаком .
/tmp/\*.out
Другой специальный символ для сопоставления — вопросительный знак ( ? ) который соответствует одному символу.
Например, source.? будет соответствовать source.c или source.o , но не source.py или source.cpp .
Последний из трех подстановочных символов для сопоставления — квадратные скобки: [ ].
Сопоставление может быть выполнено с любым из символов, перечисленных в квадратных скобках.
Так, шаблон x[abc]y соответствует любому или всем файлам с именами xay , xby или xcy при условии, что они существуют.
Вы можете указать диапазон в квадратных скобках, например: [0–9] для всех цифр.
Если первый символ в скобках — восклицательный знак ( ! ) или «шляпка» ( ^ ), то шаблон определяет все что угодно, кроме оставшихся символов в скобках.
[^aeiou] — любые символам (включая цифры и знаки пунктуации), кроме гласных.
Таблица классов символов.
[:alnum:] Алфавитно-цифровой
[:alpha:] Буквенный
[:ascii:] ASCII (американский стандартный код для обмена информацией)
[:blank:] Пробел и символ табуляции
[:ctrl:] Управляющий символ
[:digit:] Число
[:graph:] Все что угодно, кроме управляющих символов и пробела
[:lower:] Символы в нижнем регистре
[:print:] Все, кроме управляющих символов
[:punct:] Символы пунктуации
[:space:] Пробелы, включая разрывы строк
[:upper:] Символы в верхнем регистре
[:word:] Буквы, цифры и символ подчеркивания
[:xdigit:] Шестнадцатеричный символ
Пример.
*[[:punct:]]jpg
Cоответствует любому имени файла, имеющему любое количество любых символов, за которыми следуют знаки пунктуации, а за ними — буквы jpg.
Таким образом, он будет соответствовать файлам с именами wow!Jpg , some,jpg или photo.jpg , но не файлу this.is.myjpg , потому что прямо перед jpg нет знака пунктуации.
Практика. Поиск файлов, указанного типа.
Создадим скрипт для поиска со следующими параметрами.
Сигнатура:
typesearch.sh [-c dir] [-i] [-R|r] <pattern> <path>
-c Копировать найденные файлы в каталог
-i Игнорировать регистр
-R|r Рекурсивный поиск подкаталогов
Сперва определим переменную глубины поиска по умолчанию.
DEEPORNOT="-maxdepth 1"
Анализ аргументов командной строки.
while getopts 'c:irR' opt; do
case "${opt}" in
c) # копировать найденные файлы в указанный каталог
COPY=YES
DESTDIR="$OPTARG"
;;
i) # игнорировать регистр при поиске
CASEMATCH='-i'
;;
[Rr]) # рекурсивно
unset DEEPORNOT;;
*) # неизвестный/неподдерживаемый вариант
# при получении ошибки mesg от gretops просто выйти
exit 2 ;;
esac
done
‘c:irR’ - двоеточие указывает на присутствие аргумент для параметра
;; - ограничитель в операторе выбора case
Когда команда getopts анализирует параметр с указанным выше аргументом, она помещает данный параметр в переменную с именем OPTARG.
Мы ее сохраняем в DESTDIR , так как при следующем вызове getopts переменная OPTARG может быть изменена.
Поиск и копирование файлов.
shift $((OPTIND - 1))
PATTERN=${1:-PDF document}
STARTDIR=${2:-.}
# по умолчанию начать здесь
find $STARTDIR $DEEPORNOT -type f | while read FN
do
file $FN | egrep -q $CASEMATCH "$PATTERN"
if (( $? == 0 ))
# найден один
then
echo $FN
if [[ $COPY ]]
then
cp -p $FN $DESTDIR
fi
fi
done
Закончив анализ параметров, мы можем избавиться от аргументов, обработанных с помощью shift.
Разовое выполнение shift избавляет лишь от одного аргумента.
В этом случае аргумент, расположенный вторым, становится первым, третий становится вторым и т. д. Указание количества повторений команды shift , например shift5 , позволит избавиться от первых пяти аргументов и в результате аргумент $6 станет $1 , $7 станет $2 и т. д
shift $((OPTIND - 1)) - мы сдвигаем
${1:-PDF document} - при помощи : задаем значение по умолчанию если параметр не задан
-type f - искать только простые файлы (не каталоги)
read - команда, которая читает содержимое строки в переменную
find $STARTDIR $DEEPORNOT -type f | while read FN
Передаем найденные файлы в while и read-ом присваиваем переменной FN