Язык shell в Linux
Sourcing Files
Точка (.) обычно встречается в файлах автозагрузки.
В файле .profile можно увидеть следующий отрывок кода
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
В коде выше if гарантирует, что файл $HOME/.bashrc — если он существует (-f) — будет обработан (то есть прочитан и выполнен) при входе в систему
Можно использовать . всякий раз, когда изменяем файл запуска и хотим, чтобы изменения вступили в силу без перезагрузки
. ~/.bashrc
Команда source является синонимом .. Таким образом, чтобы выполнить команду source для ~/.bashrc, можно сделать так:
source ~/.bashrc
Переменные
Bash делит переменные на локальные (которые существуют только в рамках оболочки, в которой они были созданы) и глобальные (которые наследуются дочерними оболочками и/или процессами)
В Bash присвоение значения имени называется присваиванием переменной
Процесс обращения к значению, содержащемуся в имени, называется ссылкой на переменную.
Имя переменной может содержать буквы (a-z,A-Z), цифры (0-9) и символы подчеркивания (_)
При обращении к переменной, значение которой содержит начальные (или дополнительные) пробелы, иногда в сочетании со звездочками, необходимо использовать двойные кавычки после команды echo, чтобы избежать разделения поля и расширения пути:
$ lizard=" род | uros"
$ echo $lizard
род | uros
$ echo "$lizard"
род | uros
Если в ссылке на переменную стоит закрывающий восклицательный знак, он должен быть последним символом в строке (иначе Bash решит, что мы ссылаемся на событие history):
$ distro=zorin.?/!os
-bash: !os: событие не найдено
$ distro=zorin.?/!
$ echo $distro
zorin.?/!
Все обратные косые черты необходимо экранировать еще одной обратной косой чертой. Кроме того, если обратная косая черта является последним символом в строке и мы ее не экранируем, Bash интерпретирует ее как символ перевода строки и добавляет новую строку
$ distro=zorinos\
>
$ distro=zorinos\\
$ echo $distro
zorinos\
Локальные переменные или переменные оболочки
Локальные переменные или переменные оболочки существуют только в той оболочке, в которой они были созданы. По традиции локальные переменные записываются строчными буквами.
В некоторых случаях — например, при написании скриптов — неизменяемость может быть полезной особенностью переменных. Если мы хотим, чтобы наши переменные были неизменяемыми, мы можем создать их readonly
readonly reptile=черепаха
При попытке изменить такую переменную, получим ошибку: переменная доступна только для чтения
Чтобы вывести список всех переменных, доступных только для чтения в текущем сеансе, введите в терминале readonly или readonly -p
set выводит все переменные и функции оболочки, назначенные на данный момент
#Просмотр всех назначенных переменных
set | less
reptile — будучи локальной переменной — не будет унаследована дочерними процессами, запущенными из текущей оболочки
chich@T460:~$ bash
chich@T460:~$ set | grep reptile
Чтобы удалить все переменные (локальные или глобальные), мы используем команду unset
unset reptile
Глобальные переменные или переменные среды
Глобальные переменные или переменные среды существуют для текущей оболочки, а также для всех последующих процессов, порожденных ею. По традиции переменные среды записываются заглавными буквами:
SHELL
Чтобы локальная переменная оболочки стала переменной среды (в переменную окружения), необходимо использовать команду export, чтобы дочерние оболочки могли ее распознавать и использовать
#Экспорт переменной
export reptile
#export можно использовать для одновременного присвоения и экспорта переменной
export amphibian=лягушка
#переменная будет преобразована обратно в локальную переменную оболочки
export -n reptile
#export также выводит список всех существующих переменных среды, если ввести ее без параметров
export
#Команда declare -x эквивалентна export.
chich@T460:~$ declare -x reptile
chich@T460:~$ bash
chich@T460:~$ set | grep reptile
reptile=черепаха
chich@T460:~$ export | grep reptile
declare -x reptile="черепаха"
chich@T460:~$
#Еще две команды, с помощью которых можно вывести список всех переменных среды
env
printenv
printenv HOME
Используйте команду export, чтобы добавить новый каталог в переменную PATH (после перезагрузки изменения сохранятся).
Вы можете временно добавить в переменную PATH новый каталог (например, myfiles в вашем домашнем каталоге) с помощью export PATH="/home/yourname/myfiles:$PATH"
export PATH="/home/yourname/myfiles:$PATH"
Чтобы запустить новую сессию Bash с максимально пустой средой — без большинства переменных (а также функций и псевдонимов), — мы будем использовать env с опцией -i:
env -i bash
echo $USER
env
Можно использовать env для установки определенной переменной для конкретной программы
Скрипты не считывают стандартные файлы запуска, а вместо этого ищут значение переменной BASH_ENV и используют его в качестве файла запуска, если он существует.
Создадим собственный файл запуска под названием .startup_script со следующим содержимым:
CROCODILIAN=caiman
Мы пишем Bash-скрипт с именем test_env.sh со следующим содержимым:
#!/bin/bash
echo $CROCODILIAN
#Добавим бит исполняемого файла
chmod +x test_env.sh
#используем env для установки BASH_ENV в .startup_script при выполнении test_env.sh
env BASH_ENV=/home/user/.startup_script ./test_env.sh
Общие Переменные среды
DISPLAY - Значение этой переменной, связанной с сервером X, обычно состоит из трех элементов:
Имя хоста (его отсутствие означает localhost), на котором запущен X-сервер.
Двоеточие в качестве разделителя.
Число (обычно 0 и обозначает дисплей компьютера).
Пустое значение этой переменной означает, что сервер не использует X Window System. Дополнительное число — например, my.xserver:0:1 — указывает на номер экрана, если их несколько.
HISTFILE - Имя файла, в котором хранятся все введенные команды.
HISTCONTROL - определяет, какие команды будут сохранены в HISTFILE. Возможны три варианта:
ignorespace - Команды, начинающиеся с пробела, не будут сохранены.
ignoredups - Команда, совпадающая с предыдущей, не будет сохранена.
ignoreboth - Команды, относящиеся к одной из двух предыдущих категорий, не будут сохранены.
HISTSIZE - Этот параметр задает количество команд, которые будут храниться в памяти в течение сеанса работы с оболочкой.
HISTFILESIZE - количество команд, которые будут сохранены в HISTFILE в начале и в конце сеанса
HOME - Эта переменная хранит абсолютный путь к домашнему каталогу текущего пользователя и устанавливается при входе пользователя в систему.
#фрагмент кода — из ~/.profile
## включить .bashrc, если он существует
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
HOSTNAME - В этой переменной хранится TCP/IP-имя хост-компьютера
HOSTTYPE - Здесь хранится информация об архитектуре процессора основного компьютера
LANG - Эта переменная сохраняет языковой стандарт системы
LD_LIBRARY_PATH - Эта переменная состоит из набора каталогов, разделенных двоеточиями, в которых находятся разделяемые библиотеки для программ
MAIL - В этой переменной хранится файл, в котором Bash ищет электронную почту
MAILCHECK - Эта переменная содержит числовое значение, указывающее в секундах частоту, с которой Bash проверяет наличие новых писем
PATH - В этой переменной среды хранится список каталогов, в которых Bash ищет исполняемые файлы при запуске любой программы.
if [ "`id -u`" -eq 0 ]; then
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ещё
PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
fi
export PATH
В этой переменной хранится значение приглашения Bash. В следующем фрагменте кода (также из /etc/profile), оператор if проверяет личность пользователя и в зависимости от результата выводит очень сдержанное приглашение
if [ "`id -u`" -eq 0 ]; then
PS1='# '
ещё
PS1='$ '
fi
PS2 - Обычно устанавливается на > и используется в качестве подсказки для продолжения длинных многострочных команд.
PS3 - Используется в качестве подсказки для команды select
SHELL - Эта переменная содержит абсолютный путь к текущей оболочке
SSH_CONNECTION - Ссылка на переменную среды, в которой хранится информация о ssh соединениях
#Добавить каталог для исполняемых файлов в переменную среды
PATH=$PATH:/home/carol/scripts
Псевдоним
Псевдоним — это альтернативное название другой команды (других команд). Он может запускаться как обычная команда, но вместо этого выполняет другую команду в соответствии с определением псевдонима.
alias alias_name=команда
Например:
alias oldshell=sh
Преимущество псевдонимов в том, что они позволяют писать короткие версии длинных команд:
alias ls='ls --color=auto'
Чтобы получить информацию о ls и его цветах, введите man dir_colors в терминале.
#Команда alias выведет список всех доступных псевдонимов в системе:
alias
#Команда unalias удаляет псевдонимы.
unalias git-info
#Примеры
alias
alias ls='ls --color=auto'
alias oldshell='sh'
alias greet='echo Hello world!'
alias ll='ls -al'
Как и в случае с переменными, чтобы псевдонимы сохранялись, мы должны добавить их в скрипты инициализации, которые запускаются при старте системы. Как мы уже знаем, пользователям лучше всего добавлять свои псевдонимы в файл ~/.bashrc
function
После добавления псевдонимов или функций в любой файл скрипта запуска необходимо выполнить его с помощью . или source, чтобы изменения вступили в силу, если вы не хотите выходить из системы и снова входить в нее или перезагружать ее.
Функции только для чтения — это функции, содержимое которых мы не можем изменить
readonly -f my_fun
Специальные встроенные переменные Bash
Bourne Again Shell поставляется с набором специальных переменных, которые особенно полезны для функций и скриптов. Они являются специальными, потому что на них можно только ссылаться, но нельзя присваивать им значения. Вот список наиболее важных из них:
$? - результат выполнения последней команды
$$ - Расшифровывается как PID (идентификатор процесса) оболочки
$! - PID последнего фонового задания (амперсанд (&) используется для запуска процессов в фоновом режиме.)
$0 по $9 - параметры и аргументы передаваемые скриптам
$# - кол-во аргументов передаваемых в команде
$@, $* - кол-во аргументов передаваемых в команде
Файл сценария — это упорядоченная последовательность команд, которые должны быть выполнены соответствующим командным интерпретатором
Интерпретатор может по-разному считывать файл сценария, и в сеансе работы с оболочкой Bash это можно сделать разными способами, но по умолчанию интерпретатором для файла сценария будет тот, который указан в первой строке сценария сразу после символов #! (так называемый шебанг)
Bash вызовет команду, указанную после #!, в качестве интерпретатора для файла сценария. Это может быть полезно, например, для запуска других скриптовых языков, таких как Python (#!/usr/bin/python), Perl (#!/usr/bin/perl) или awk (#!/usr/bin/awk).
У скрипта, выполняющего действия с ограниченными правами, может быть активировано разрешение SUID, поэтому обычные пользователи также могут запускать скрипт с правами root. В этом случае очень важно убедиться, что ни у кого, кроме пользователя root, нет прав на запись в файл. В противном случае обычный пользователь может изменить файл, чтобы выполнять произвольные и потенциально опасные операции.
При выполнении скрипта содержащиеся в нем команды запускаются не в текущем сеансе, а в новом процессе Bash, который называется под-оболочкой. Это предотвращает перезапись переменных среды текущего сеанса скриптом и внесение изменений в текущий сеанс без участия пользователя. Если нужно запустить содержимое скрипта в текущем сеансе оболочки, его следует запускать с помощью source script.sh или . script.sh (обратите внимание, что между точкой и названием скрипта есть пробел).
Аргументы
Аргументы, передаваемые в скрипт, и другая полезная информация хранятся в параметрах $0, $*, $? и т. д., где символ после знака доллара указывает на извлекаемую информацию:
$* - Все аргументы, переданные в скрипт.
$@ - Все аргументы, переданные в скрипт. При использовании с двойными кавычками, как в "$@", каждый аргумент будет заключен в двойные кавычки.
$# - Количество аргументов.
$0 - Имя файла скрипта.
$! - PID последней запущенной программы.
$$ - PID текущей оболочки.
$? - Числовой код состояния выхода последней выполненной команды. Для стандартных процессов POSIX числовое значение 0 означает, что последняя команда была успешно выполнена. Это также относится к сценариям командной оболочки.
Позиционный параметр - это параметр, обозначаемый одной или несколькими цифрами, отличными от однозначной 0
Например, переменная $1 соответствует первому аргументу, заданному скрипту (позиционный параметр one), $2 соответствует второму аргументу и так далее. Если позиция параметра больше девяти, ссылка на него должна быть заключена в фигурные скобки, как в ${10}, ${11} и т.д.
команда read может использоваться в скрипте для запроса ввода у пользователя во время выполнения скрипта:
echo "Вы хотите продолжить (y/n)?"
read ANSWER
#можно использовать команду read для одновременного чтения нескольких переменных:
echo "Введите свое имя и фамилию:"
read NAME SURNAME
#read может самостоятельно вывести сообщение для пользователя с помощью опции -p
read -p "Введите свое имя и фамилию:" ИМЯ ФАМИЛИЯ
Для сохранения вывода команды в переменной можно использовать обратные кавычки:
$ OS=`uname -o`
Аналогичный результат будет получен с помощью $():
$ OS=$(uname -o)
Чтобы узнать длину переменной, то есть количество содержащихся в ней символов, нужно добавить хэш # перед ее названием. Однако для использования этой функции необходимо указывать переменную в фигурных скобках:
$ OS=$(uname -o)
$ echo $OS
GNU/Linux
$ echo ${#OS}
9
В Bash также есть одномерные массивы, поэтому набор связанных элементов можно хранить в одной переменной.
Каждый элемент массива имеет числовой индекс, который необходимо использовать для записи и чтения значений соответствующего элемента.
В отличие от обычных переменных, массивы объявляются с помощью встроенной команды Bash declare.
#объявить переменную SIZES массивом:
declare -a SIZES
Массивы также могут быть объявлены неявно при заполнении из заранее заданного списка элементов с помощью скобок
На элементы массива нужно ссылаться с помощью фигурных и квадратных скобок, иначе Bash не сможет корректно изменить или отобразить элемент. Поскольку индексы массива начинаются с 0, содержимое первого элемента находится в ${SIZES[0]}, второго — в ${SIZES[1]} и так далее.
В отличие от чтения, изменение содержимого элемента массива выполняется без фигурных скобок (например, SIZES[0]=1048576)
#Неявное объявление массивов
SIZES=( 1048576 1073741824 )
echo ${SIZES[1]}
#Присвоить значение элементу массива
SIZES[0]=1048576
#длина элемента массива
echo ${#SIZES[0]}
#@ или *, возвращается общее количество элементов в массиве:
echo ${#SIZES[*]}
Массивы также можно объявлять, используя вывод команды в качестве начальных элементов с помощью подстановки команд
В следующем примере показано, как создать массив Bash, элементами которого являются поддерживаемые файловые системы текущей системы:
FS=( $(cut -f 2 < /proc/filesystems) )
echo ${FS[3]}
proc
Для инициализации массива можно использовать любое текстовое содержимое, поскольку по умолчанию все элементы, разделенные символами пробел, табуляция или новая строка, становятся элементами массива.
Bash воспринимает каждый символ в $IFS (разделителе полей ввода) переменной окружения как разделитель. Чтобы изменить разделитель полей на символы новой строки, нужно сбросить переменную IFS с помощью команды IFS=$'\n'.
Модель TCP/IP, так же как и модель OSI, имеет многоуровневую структуру, но для того, чтобы данные от приложения компьютера А были переданы приложению на компьютере B, они должны последовательно пройти 4 уровня: уровень приложений, транспортный уровень, уровень Интернет и уровень доступа к среде.
Трем верхним уровням модели OSI соответствует уровень приложений (Application layer) в модели TCP/IP, который включается в себя функции представления, кодирования и контроля над установлением соединения. Существует множество протоколов уровня приложений, из которых самыми распространенными являются FTP, TFTP, HTTP/HTTPs, DHCP, DNS, Telnet, SMTP, POP3, IMAP и др.
Транспортный уровень (Transport layer) модели TCP/IP выполняет те же функции, что и одноименный уровень в модели OSI.
Уровень доступа к среде (Network access layer) объединяет функции канального и физического уровня модели OSI, обеспечивая физическую передачу данных в сети. Существует множество различных протоколов уровня доступа к сети, из которых самыми распространенными являются Ethernet, Token Ring, FDDI, PPP, IEEE 802.11 (Wi-Fi), ATM и др.
Арифметические выражения
Bash предоставляет удобный способ выполнения целочисленных арифметических операций с помощью встроенной команды expr
Команду expr можно заменить на $(())
#Сложение переменных
SUM=`expr $VAL1 + $VAL2`
#$(())
SUM=$(( $VAL1 + $VAL2 ))
#Выражения со степенями
SIZES=( $((1024**2)) $((1024**3)) )
#Умножение
#количество свободных байт в ОЗУ
FREE=$(( 1000 * `sed -nre '2s/[^[:digit:]]//gp' < /proc/meminfo` ))
Условное выполнение
Если разделить команды с помощью &&, то команда справа будет выполнена только в том случае, если команда слева не вызвала ошибку, то есть если ее код завершения был равен 0
КОМАНДА A && КОМАНДА B && КОМАНДА C
Если команды разделены символом ||., происходит обратное: следующая команда будет выполнена только в том случае, если предыдущая вызвала ошибку, то есть если ее код состояния отличается от 0.
КОМАНДА A || КОМАНДА B
Символ новой строки \n можно использовать для разделения строк вывода, поэтому тот же результат можно получить, объединив две команды echo в одну:
echo -e "Операционная система:\t$OS\nНераспределенная оперативная память:\t$(( $FREE / 1024**2 )) МБ"
Встроенная в Bash команда printf позволяет лучше контролировать отображение переменных. Команда printf использует первый аргумент в качестве формата вывода, где заполнители заменяются следующими аргументами в порядке их следования в командной строке. Например, сообщение из предыдущего примера можно сгенерировать с помощью следующей команды printf:
printf "Операционная система:\t%s\nНераспределенная оперативная память:\t%d МБ\n" $OS $(( $FREE / 1024**2 ))
Формат подстановки заполнителей, выполняемой с помощью printf, можно настроить, используя тот же формат, что и в функции printf языка программирования C. Полное описание функции printf можно найти на странице руководства, доступ к которой осуществляется с помощью команды man 3 printf.
При использовании printf переменные размещаются за пределами текстового шаблона, что позволяет сохранить текстовый шаблон в отдельной переменной
MSG='Операционная система:\t%s\nСвободная оперативная память:\t%d МБ\n'
printf "$MSG" $OS $(( $FREE / 1024**2 ))
Ввод пароля
Опция -s для команды read полезна при вводе паролей, так как не отображает вводимый текст на экране.
Команда test или if
#Проверим что каталог это каталог
test -d /etc
#существует ли путь в VAR в файловой системе и является ли он файлом.
VAR=/usr/bin/ip
-a "$VAR"
#является ли путь в VAR специальным блочным файлом.
test -b "$VAR"
#является ли путь в VAR файлом со специальным символом
test -c "$VAR"
# является ли путь в VAR каталогом.
test -d "$VAR"
#существует ли путь в VAR в файловой системе
test -e "$VAR"
#существует ли путь в VAR и является ли он обычным файлом
test -f "$VAR"
#Проверьте, есть ли у пути в VAR разрешение SGID
test -g "$VAR"
#является ли путь в VAR символической ссылкой
test -h "$VAR"
# является ли путь в VAR символической ссылкой (например, -h)
test -L "$VAR"
#есть ли у пути в VAR разрешение sticky
test -k "$VAR"
#является ли путь в VAR файлом pipe
test -p "$VAR"
#доступен ли путь в VAR для чтения текущему пользователю
test -r "$VAR"
# существует ли путь в VAR и не является ли он пустым.
-s "$VAR"
# является ли путь в VAR файлом сокета
test -S "$VAR"
#открыт ли путь в VAR в терминале
test -t "$VAR"
#есть ли у пути в VAR разрешение SUID
test -u "$VAR"
#доступен ли путь в VAR для записи текущему пользователю
test -w "$VAR"
#доступен ли путь в VAR для выполнения текущим пользователем
test -x "$VAR"
#принадлежит ли путь в VAR текущему пользователю
test -O "$VAR"
# относится ли путь в VAR к эффективной группе текущего пользователя
-G "$VAR"
#был ли изменен путь в VAR с момента последнего обращения к нему
test -N "$VAR"
#Сравните даты изменения файлов в VAR1 и VAR2 и определите, какой из них новее
test "$VAR1" -ot "$VAR2"
#не является ли путь в VAR1 более старым, чем VAR2
test "$VAR1" -ot "$VAR2"
#выражение возвращает значение True, если путь в VAR1 является жесткой ссылкой на VAR2
test "$VAR1" -ef "$VAR2"
Рекомендуется заключать тестируемую переменную в двойные кавычки, так как в случае, если переменная окажется пустой, это может привести к синтаксической ошибке в команде test. Для параметров теста требуется аргумент-операнд, и пустая переменная без кавычек приведет к ошибке из-за отсутствия обязательного аргумента. Существуют также тесты для произвольных текстовых переменных, которые описываются следующим образом:
#не является ли переменная TXT пустой
test -z "$TXT"
#не является ли переменная TXT пустой
test -n "$TXT" или test "$TXT"
#равны ли TXT1 и TXT2
test "$TXT1" = "$TXT2"
#or
test "$TXT1" == "$TXT2"
#не равны ли TXT1 и TXT2
test "$TXT1" != "$TXT2"
#стоит ли TXT1 перед TXT2, в алфавитном порядке
test "$TXT1" > "$TXT2"
#идет ли TXT1 после TXT2, в алфавитном порядке
test "$TXT1" > "$TXT2"
В разных языках могут быть разные правила алфавитного упорядочивания
Для числовых сравнений предусмотрен собственный набор вариантов тестирования:
#меньше ли NUM1 чем NUM2
test $NUM1 -lt $NUM2
#больше ли NUM1 чем NUM2
test $NUM1 -gt $NUM2
#меньше ли NUM1 или равно NUM2
test $NUM1 -le $NUM2
#больше ли NUM1 или равно NUM2
test $NUM1 -ge $NUM2
#равно ли NUM1 NUM2
test $NUM1 -eq $NUM2
#не равно ли NUM1 NUM2
test $NUM1 -ne $NUM2
#является ли выражение EXPR ложным
! EXPR
#верны ли оба утверждения: EXPR1 и EXPR2
EXPR1 -a EXPR2
#истинно ли хотя бы одно из двух выражений
EXPR1 -o EXPR2
Конструкция case
Еще одну условную конструкцию, case, можно рассматривать как разновидность конструкции if. Инструкция case выполнит список заданных команд, если указанный элемент — например, содержимое переменной — будет найден в списке элементов
В следующем примере показано, как с помощью конструкции case можно указать соответствующий формат упаковки программного обеспечения для определенного дистрибутива Linux:
#!/bin/bash
DISTRO=$1
echo -n "Дистрибутив $DISTRO использует "
case "$DISTRO" in
debian | ubuntu | mint)
echo -n "пакет DEB"
;;
centos | fedora | opensuse )
echo -n "пакет RPM"
;;
*)
echo -n "неизвестный пакет"
;;
esac
echo "формат пакета."
В Bash есть опция nocasematch , которая включает сопоставление с образцом без учета регистра для конструкции case и других условных команд. Встроенная команда shopt переключает значения параметров, управляющих дополнительными функциями оболочки: shopt -s включает (set) заданную опцию, а shopt -u отключает (unset) заданную опцию. Таким образом, если поместить shopt -s nocasematch перед конструкцией case, будет включено сопоставление с образцом без учета регистра. Параметры, измененные shopt, повлияют только на текущий сеанс, поэтому измененные параметры внутри скриптов, запущенных во вложенной оболочке, что является стандартным способом запуска скрипта, не влияют на параметры родительского сеанса.
Искомый элемент и шаблоны подвергаются подстановке тильды, подстановке параметров, подстановке команд и арифметической подстановке.
Циклические конструкции
Bash есть три инструкции для циклов — for, until и while, — предназначенные для немного отличающихся друг от друга конструкций циклов.
Конструкция for перебирает заданный список элементов — обычно это список слов или любых других текстовых сегментов, разделенных пробелами, — выполняя один и тот же набор команд для каждого из этих элементов. Перед каждой итерацией инструкция for присваивает текущий элемент переменной, которую затем могут использовать вложенные команды. Процесс повторяется до тех пор, пока не останутся только пустые элементы. Синтаксис конструкции for выглядит следующим образом:
for VARNAME in LIST
do
КОМАНДЫ
done
Bash также поддерживает альтернативный формат конструкций for с использованием двойных скобок. Этот формат напоминает синтаксис инструкций for в языке программирования C и особенно удобен для работы с массивами:
#!/bin/bash
SEQ=( 1 1 2 3 5 8 13 )
for (( IDX = 0; IDX < ${#SEQ[*]}; IDX++ ))
do
echo -n "${SEQ[$IDX]} is "
if [ $(( ${SEQ[$IDX]} % 2 )) -ne 0 ]
then
echo "odd."
else
echo "even."
fi
done
Аналогичным образом конструкция until выполняет последовательность команд до тех пор, пока тестовая команда — как и сама команда test — не завершится со статусом 0 (успешно)
#!/bin/bash
SEQ=( 1 1 2 3 5 8 13 )
IDX=0
until [ $IDX -eq ${#SEQ[*]} ]
do
echo -n "${SEQ[$IDX]} is "
if [ $(( ${SEQ[$IDX]} % 2 )) -ne 0 ]
then
echo "odd."
else
echo "even."
fi
IDX=$(( $IDX + 1 ))
done
Mapfile (также известна как readarray) — встроенная команда командной оболочки Bash в Linux и Unix-подобных операционных системах. Она считывает строки из стандартного ввода или файлового дескриптора в переменную индексированного массива.
Строки читаются непосредственно в массив без использования цикла, что быстрее для обработки больших файлов.
С помощью опции -t mapfile автоматически удаляет символ новой строки (по умолчанию) с конца каждой строки, что экономит дополнительный шаг в обработке
#!/bin/bash
set -ef
# Список элементов для синхронизации
FILE=~/.sync.list
# Исходный каталог
FROM=$1
# Целевой каталог
TO=$2
# Проверяем, существуют ли оба каталога
if [ ! -d "$FROM" -o ! -d "$TO" ]
then
echo Использование:
echo "$0 "
exit 1
fi
# Создание массива из файла
mapfile -t LIST < $FILE
# Синхронизация элементов
for (( IDX = 0; IDX < ${#LIST[*]}; IDX++ ))
do
echo -e "$FROM/${LIST[$IDX]} \u2192 $TO/${LIST[$IDX]}";
rsync -qa --delete "$FROM/${LIST[$IDX]}" "$TO";
done
Первое действие скрипта — переопределение двух параметров оболочки с помощью команды set: опция -e немедленно завершает выполнение, если команда завершается с ненулевым кодом выхода, а опция -f отключает подстановку имен файлов. Обе опции можно сократить до -ef. Это необязательный шаг, но он помогает снизить вероятность непредвиденного поведения.
FILEПеременная - это путь к файлу, содержащему список элементов, подлежащих копированию
После определения всех параметров с помощью команды mapfile -t LIST < $FILE. создается массив, содержащий список элементов для копирования. Опция -t команды mapfile удаляет завершающий символ новой строки из каждой строки перед включением в переменную массива с именем LIST
Aикл for с использованием нотации с двойными круглыми скобками перебирает массив элементов, а переменная IDX отслеживает увеличение индекса.
В выходном сообщении присутствует экранированный символ Юникода — \u2192 — для символа стрелка вправо, поэтому необходимо использовать опцию -e команды echo
Параметр --delete приведет к rsync удалению элемента в целевом каталоге, которого больше нет в исходном каталоге, поэтому его следует использовать с осторожностью.
Дополнительно
Как можно использовать все аргументы командной строки скрипта для инициализации массива Bash?
Команды PARAMS=( $* ) или PARAMS=( "$@" ) создают массив с именем PARAMS со всеми аргументами.
Как пользователю временно изменить разделитель полей по умолчанию на символ новой строки, чтобы при этом иметь возможность вернуть его в исходное состояние?
Копию переменной IFS можно сохранить в другой переменной: OLDIFS=$IFS. Затем с помощью IFS=$'\n' можно задать новый разделитель строк, а с помощью IFS=$OLDIFS. — вернуть переменную IFS в исходное состояние.
Использование кавычек для переменных типа строка
hello="A B C D"
echo $hello # A B C D
echo "$hello" # A B C D
# Здесь вы сможете наблюдать различия в выводе echo $hello и echo "$hello".
устая команда. [двоеточие]
Пустая команда. [двоеточие] Это эквивалент операции "NOP" (no op, нет операции). Может рассматриваться как синоним встроенной команды true. Команда ":" так же является встроенной командой Bash, которая всегда возвращает "true"
#Бесконечный цикл:
while :
do
operation-1
operation-2
...
operation-n
done
# То же самое:
# while true
# do
# ...
# done
#Символ-заполнитель в условном операторе if/then:
if condition
then : # Никаких действий не производится и управление передается дальше
else
take-some-action
fi
#В операциях с подстановкой параметров
#Вывод сообщения об ошибке, если одна или более переменных не определены.
: ${HOSTNAME?} ${USER?} ${MAIL?}
Круглые скобки
Команды, заключенные в круглые скобки исполняются в дочернем процессе -- subshell-е.
Круглые скобки используются также для инициализация массивов.
#группа команд.
(a=hello; echo $a)
#инициализация массивов.
Array=(element1 element2 element3)
Фигурные скобки
# Поиск всех вхождений слова "Linux"
grep Linux file*.{txt,htm*}
#Подстановка
echo {file1,file2}\ :{\ A," B",' C'}
Блок кода. [фигурные скобки] Известен так же как "вложенный блок", эта конструкция, фактически, создает анонимную функцию. Однако, в отличии от обычных функций, переменные, создаваемые во вложенных блоках кода, доступны объемлющему сценарию.
{ local a; a=123; }
Код, заключенный в фигурные скобки, может выполнять перенаправление ввода-вывода.
#!/bin/bash
# Чтение строк из файла /etc/fstab.
File=/etc/fstab
{
read line1
read line2
} < $File
echo "Первая строка в $File :"
echo "$line1"
echo
echo "Вторая строка в $File :"
echo "$line2"
exit 0
Сохранение результата исполнения вложенного блока в файл
{
echo "Описание архива:"
} > "file.txt" # Перенаправление вывода в файл.
{} \; - pathname -- полное имя файла (т.е. путь к файлу и его имя). Чаще всего используется совместно с командой find.
Принудительное перенаправление
>|
принудительное перенаправление, даже если установлен ключ noclobber option.
Источник: https://www.opennet.ru/docs/RUS/bash_scripting_guide/c301.html
Использование префикса "-" в перенаправлениях
В случае, когда ожидается имя файла, тогда "-" перенаправляет вывод на stdout
Перемещение полного дерева файлов и подкаталогов из одной директории в другую
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
# 1) cd /source/directory Переход в исходный каталог, содержимое которого будет перемещено
# 2) && "И-список": благодаря этому все последующие команды будут выполнены
# только тогда, когда 'cd' завершится успешно
# 3) tar cf - . ключом 'c' архиватор 'tar' создает новый архив,
# ключом 'f' (file) и последующим '-' задается файл архива -- stdout,
# в архив помещается текущий каталог ('.') с вложенными подкаталогами.
# 4) | конвейер с ...
# 5) ( ... ) subshell-ом (дочерним экземпляром командной оболочки)
# 6) cd /dest/directory Переход в каталог назначения.
# 7) && "И-список", см. выше
# 8) tar xpvf - Разархивирование ('x'), с сохранением атрибутов "владельца" и прав доступа ('p') к файлам,
# с выдачей более подробных сообщений на stdout ('v'),
# файл архива -- stdin ('f' с последующим '-').
#
# Примечательно, что 'x' -- это команда, а 'p', 'v' и 'f' -- ключи
Обратите внимание, что в этом контексте "-" - не самостоятельный оператор Bash, а скорее опция, распознаваемая некоторыми утилитами UNIX (такими как tar, cat и т.п.), которые выводят результаты своей работы в stdout.
echo "whatever" | cat -
Резервное архивирование всех файлов, которые были изменены в течение последних суток
Резервное архивирование всех файлов, которые были изменены в течение последних суток
#!/bin/bash
# Резервное архивирование (backup) всех файлов в текущем каталоге,
# которые были изменены в течение последних 24 часов
#+ в тарболл (tarball) (.tar.gz - файл).
BACKUPFILE=backup
archive=${1:-$BACKUPFILE}
# На случай, если имя архива в командной строке не задано,
#+ т.е. по-умолчанию имя архива -- "backup.tar.gz"
tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
gzip $archive.tar
echo "Каталог $PWD заархивирован в файл \"$archive.tar.gz\"."
# Stephane Chazelas заметил, что вышеприведенный код будет "падать"
#+ если будет найдено слишком много файлов
#+ или если имена файлов будут содержать символы пробела.
# Им предложен альтернативный код:
# -------------------------------------------------------------------
# find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
# используется версия GNU утилиты "find".
# find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \;
# более универсальный вариант, хотя и более медленный,
# зато может использоваться в других версиях UNIX.
# -------------------------------------------------------------------
exit 0
Источник: https://www.opennet.ru/docs/RUS/bash_scripting_guide/c301.html
Классы символов
[:upper:] - Символы в верхнем регистре
[^[:upper:]] - Кроме символов в верхнем регистре
[:digit:] - Десятичные цифры
[^- [:digit:]\(\)] - Кроме цифр, скобок, дефисов
Перевод первого символа строки в верхний регистр:
name=toly; echo $name | cut -c1 | tr '[:lower:]' '[:upper:]'
echo 347.22 | cut -d. -f1 #получить значения слева от точки
echo 347.22 | cut -d. -f2 #получить значения справа от точки
Цикл работает пока num больше 5, в теле уменьшается число num:
num=10; while [ $num -gt 5 ]; do echo $num; num=$(expr $num - 1); done
#getops необходим для выполнения команды в зависимости от выбранной опции
while getopts "a:b:" opt; do
case $opt in
a ) echo "Это опция a" ;;
b ) echo "Это опция b" ;;
esac
done
#Первый символ num, num без первого символа
num=-1; echo "${num%${num#?}}"; echo "${num#?}";
#Оставить в переменной все кроме цифр:
echo $num | sed "s/[[:digit:]]//g"
#1. Убрать все символы, кроме точки, 2. Вывод символов слева от точки, 3. Символы справа от точки:
num=10.5; echo $num | sed 's/[^.]//g'; echo $num | cut -d. -f1; echo ${num#*\.}
Замена вхождений с помощью утилиты tr
Замена регистра:
~/bash_scripts$ echo "HeLlo" | tr '[:upper:]' '[:lower:]'
hello
~/bash_scripts$ echo "HeLlo" | tr '[:lower:]' '[:upper:]'
HELLO
Если year не делится без остатка на 4, тогда...
~$ year=20; if [ "$((year % 4))" -ne 0 ] ; then echo echo 1; else echo 0; fi
0
Разбить дату на день, месяц, год:
~$ date=10/07/2022; echo $date | cut -d/ -f1
10
~$ date=10/07/2022; echo $date | cut -d/ -f2
07
~$ date=10/07/2022; echo $date | cut -d/ -f3
2022
Удалить из текста все знаки переноса:
cat test.txt | tr -d '\n'
shift сдвигает параметры на одну позицию влево, из первой переменной значение просто отбрасывается, это удобно использовать, когда неизвестно сколько параметров всего и параметры обрабатываются по очереди из первой переменной. Встроенный документ
bc << EOF
$test
$*
quit
EOF
$OPTIND - это индекс следующего обрабатываемого аргумента which - выводит полный путь до файла скрипта команды
Перебор аргументов
#!/bin/bash
for arg in "$@"
do
echo "${PREFIX}: $arg"
if [ -f ${arg} ] ; then
echo "${arg} is a file"
fi
#Либо использовать case
done
Если вызвать один сценарий командной оболочки из другого, по умолчанию он будет выполнен в собственной оболочке
t@x:~$ echo "test=2" >> tinyscript.sh
t@x:~$ chmod +x tinyscript.sh
t@x:~$ test=1
t@x:~$ ./tinyscript.sh
t@x:~$ echo $test
1
t@x:~$
Если выполнить это сценарий подключением файла с помощью точки идентичной команде source (include в языке C)FD
. tinyscript.sh
Таким образом можно подключить библиотечный файл с готовыми функциями, сценариями в один из каталогов перечисленных в окружении PATH
Для вывода отладочной информации используй bash -x script.sh
Как вариант, можно использовать set -x перед началом выполнения скрипта и set +x после выполнения скрипта
Получение случайного числа
echo $RANDOM
Вывод директорий из переменной $PATH
$ IFS=":"; for directory in $PATH ; do echo $directory; done
Источники
Связанные темы
Конфигурация vsftpd в ОС Linux Ubuntu
Установка и конфигурация samba в Linux
Оптимизация запросов в Postgresql
Оператор разрешения видимости в php
Структура каталогов linux(Debian)
Позднее статическое связывание
Язык программирования C в Linux
Буферный кэш и журнал в Postgresql
Установка и настройка MS SQL Server 2008 на Windows Server 2008 r
Использование hydra для тестирования
Методы создания экземпляра класса в php
Конфигурация openvpn сервера в Linux Alpine
Удаление файлов из bash linux с возможностью восстановления
Использование команды tee в Linux
Использование модуля pg_stat_statements в postgresql
Поиск уязвимостей и следов взлома в Linux
Системный каталог в Postgresql
Конфигурация почтового сервера
Использование iptables в linux
Определение данных в Postgresql
Изменение временной зоны в Postgresql
Сценарий инициализации в Linux
Табличные пространства в Postgresql
Управление планировщиком в Postgresql
Выполнение html и js кода в php
Программирование на языке Assembler в Linux
Основные команды psql и sql Postgresql
Методы формирования соединений наборов строк
Оптимизация производительности
Использование wget и curl в Linux
Агрегирование и группировка в Postgresql
Основные понятия реляционной модели
Автоматическая загрузка классов
Управление учетными записями пользователей в Linux
Разделяемые библиотеки в Linux
Команда chattr и lsattr в Linux
Сброс пароля root в grub (Linux)
Лексическая структура в Postgresql
Базовые понятие о настройках и безопасности в сети
Использование awk Linux Alpine
Изменить часовой пояс в Linux Alpine
Перенести кластер Postgresql-10 на Postgresql-12
Наследование с помощью extends в php
Конфигурация Postgresql-12 для удаленного подключения
Использование ifconfig в Linux
IP-телефония в компьютерных сетях
Создание и управление кластером postgresql
Системные каталоги в Postgresql
Обновление кластера Postgresql
Этапы запроса и получения результата в postgresql
Проверка и восстановление файловой системы в Linux
Настройка репликации в postgresql
Полнотекстовый поиск в Postgresql
Саздание сертификатов SSL (TLS) для сайта
Анализ производительности виртуальной машины
Мониторинг событий в linux с помощью auditd
Использование RAID массивов в Linux
Настройка виртуального сервера Nginx
Оптимизация производительности Postgresql
Конфигурация Postfix в Linux Alpine
Проверка на необходимость перезапуска после обновления пакетов
Стандартные потоки и перенаправление ввода/вывода в Linux
Конструкторы и деструкторы в php
Устройство и принцип работы Postgresql
Архивирование и сжатие файлов в Linux
Сценарии инициализации в Linux Ubuntu
Полезные команды и скрипты Linux
Использование регулярных выражений в Linux
Условная конструкция if в Linux
Использование оператора select в языке shell
Внутренние и внешние команды linux
Использование командной строки bash
Использование конструкции case в Linux
Полезные сетевые утилиты Linux
Настройка openvpn сервера в Linux
Установка сервера Apache Linux Alpine
Конфигурация сети в Alpine Linux
Compression and Decompression Nginx
Настройка ведения журнала Nginx и поиск ошибок
Преодоление разрыва соединения
Мониторинг системы Linix с помощью getconf
Использование ANCI последовательностей в Linux
Работа в командной строке Linux
Подключение к серверу Postgresql
Ковариантность и контравариантность
Создание deb пакета в Linux Ubuntu
Проблемный сертификат IdenTrust DST Root CA X3
Установка и настройка tftp сервера в Linux Ubuntu
Тест производительности с помощью pgbench в postgresql
Многоверсионность в Postgresql
Изменение кодировки базы данных в Postgresql
Использование logrotate в Linux
Использование fail2ban в Linux
Использование Glances для мониторинг в Linux
Отправка сообщения из Linux с помощью бота Telegram
Создание, регистрация, аутентификация пользователя в Symfony
Символические и жесткие ссылки






