2020
Всего можно выделить 2 типа исполняемых файла:
Для запуска первых приложений достаточно только ОС, для работы вторых нужен установленный интерпретатор или соответствующая исполнительная машина
Типичный способ запуска таких приложений выглядит следующим образом:
интерпретатор файл-приложения
python app.py
ruby app.rb
bash script.sh
Приложение, реализованное на языке Python, обычно знает какой интерпретатор ему нужен, а вот его пользователь может не знать
В UNIX-мире есть механизм, позволяющий указать интерпретатор для запуска файла
Строка для выбора интерпретатора называется Shebang:
#!интерпретатор [аргументы]
В случае, если в начале файла указана данная строка, то при его запуске загрузчик постарается найти интерпретатор и запустит интерпретатор с указанным файлом
#!/bin/sh — запуск интерпретатора Bourne Shell или совместимого с ним.#!/bin/bash — запуск файла с помощью интерпретатора Bash.#!/usr/bin/python3 — запуск файла с помощью системного интерпретатора Python 3#!/usr/bin/env ruby — запуск файла с помощью интерпретатора Ruby, поиск которого надо выполнять в PATH пользователя, вызывающего приложение#!/bin/false — запретить выполнение файла через запуск, нужно например для файлов, предназначеных только для подключения к другим исполняемым скриптамПуть к интерпретатору должен быть абсолютным, т.к. данный механизм по умолчанию не использует подсистему поиска по PATH. Если необходима функциональность по поиску интерпретатора, тогда следует использовать /usr/bin/env
Рассмотрим простейшее приложение на языке Bash:
echo 'Hello, world!'Запишем его в файл /home/user/script.sh и дадим права на исполнение
$ cd /home/user
$ chmod +x script.sh
$ ./script.sh
Для запуска скрипта необходимо:
Исполняемые скрипты для выполнения административных задач в Linux можно писать на множестве языков: Python, Ruby, Perl, Bash и так далее
При выборе языка программирования необходимо учитывать множество факторов:
Если задача требует сложных вычислений и её необходимо поддерживать достаточно долго, то рекомендуется использовать более продвинутые языки программирования
С базовым синтаксисом Bash мы уже знакомы
#!/bin/bash
touch file1
mkdir trash
mv file1 trash
rm -rf trash
mkdir trash
echo "Файл удален!"Bash выполняет каждую строку как отдельную команду, которую вводили в терминале
Наиболее полная книжка по Bash называется Advanced Bash Scripting Guide
*, ?, [[:upper:]]{abc, def}$((5 + 10))${VAR}$(ls)Все эти элементы можно и нужно использовать при написании скриптов
Для объявления переменной используется синтаксис:
name=[value]Для получения доступа к значению переменной используйте $name
alias, declare, typeset, export, readonly, localunset name#!/bin/bash
info='Some data goes here'
echo "Значение info: $info" # Просмотр переменной
files=$(ls)  # Сохранение результата вызова ls
echo "$files"$ ls
abc  test.sh
$ ./test.sh
Some data goes here
abc
test.sh
Со знака # начинаются комментарии
Для обхода набора значений удобно воспользоваться циклами
for index in 1 2 3 4 5 6 7 8 9 10; do
    echo "Индекс: $index"
donefor ((index=0; index < 10; index++)) do
    echo "Число: $index"
doneЦиклы можно использовать для обработки вывода из приложения
echo 'Файлы, начинающиеся с A'
for file in A*; do
    echo "$file"
doneecho 'Проход по PID процессов'
for pid in $(ps -eo pid); do
    echo "${pid}"
doneifБлагодаря наличию данного оператора можно говорить, что Bash является языком программирования. Важно понимать, что этот язык — узкоспециализированный, ориентированный на обработку строк и запуск внешних задач, поэтому разрабатывать на нём сложные приложения не стоит
Синтаксис:
if list; then list;
[ elif list; then list; ] ...
[ else list; ]
fi
Если последняя команда из списка в условии вернёт 0, тогда будут выполнены команды в then-списке, в противном случае будет протестированы команды из elif
Стандартное определение функции main на языке Си:
int main(int argc, char* argv[], char** envp) {...}int argc — количество аргументовchar* argv[] — массив с аргументамиchar** envp — массив с переменными окружения в формате КЛЮЧ=ЗНАЧЕНИЕint main, метод возвращает целое значение, обозначающее результат выполнения
В BASH переменная ? позволяет узнать статус выполнения последней команды
$ ls
$ echo $?
0
$ ls /aoeb &> /dev/null
$ echo $?
2testПриложение позволяет проверить некоторые выражения и вернуть 1 или 0
test ВЫРАЖЕНИЕ
[ ВЫРАЖЕНИЕ ]
Написанное выражение подвергается обработке со стороны BASH как обычная строка, а после передаётся для выполнения в приложение test
Данное приложение предназначено для использования в if-выражении:
# Testing that file exists
if [ -f /tmp/data ]; then
    echo "Файл /tmp/data существует!"
fi[[Данная команда является расширением приложения test и исправляет ряд его ограничений. Рекомендуется к использованию вместо приложения test
[[ ]] не происходит расширения строк&&, || внутри выражения(...)Команду [[ следует использовать для сравнения строк и проверки файлов
testНесколько проверок можно объединить с помощью &&, || на уровне Bash:
[ -f /tmp/data ] || { [ -f /tmp/info ] && [ -f /tmp/more ] }Операторы && и || работают на уровне результатов работы приложения
test и [[Строковые проверки отличаются
[[ | 
test | Пример | 
|---|---|---|
> | 
\> | 
[[ a > b ]] ложь, a идёт раньше b | 
< | 
\< | 
[[ az < za ]] правда, a идёт раньше z | 
=, == | 
= | 
[[ a = a ]] правда, a равно a | 
!= | 
!= | 
[[ a != b ]] правда, a не равно b | 
=, == | 
нет | [[ name = n* ]] правда, name начинается с n | 
=~ | 
нет | [[ home =~ ^h+ ]] правда, home соответствует выражению | 
-z | 
-z | 
[[ -z $info ]] правда, если строка в $info нулевой длины | 
-n | 
-n | 
[[ -n $data ]] правда, если строка в $data ненулевой длины | 
test и [[Числовые проверки между test и [[ не отличаются
| Проверка | Пример | 
|---|---|
-gt | 
[[ 5 -gt 10 ]] ложь, 5 меньше 10 | 
-lt | 
[[ 8 -lt 9 ]] правда, 8 меньше 9 | 
-eq | 
[[ 5 -eq 3 ]] ложь, 5 не равно 3 | 
-ne | 
[[ 5 -ne 3 ]] правда, 5 не равно 3 | 
-ge | 
[[ 3 -ge 3 ]] правда, 3 больше или равно 3 | 
-le | 
[[ 3 -le 8]] правда, 3 меньше или равно 8 | 
test и [[| Проверка | Пример | 
|---|---|
-a | 
[[ -a /tmp/data ]] правда, если файл существует | 
-d | 
[[ -d /var/log ]] правда, если директория существует | 
-e | 
[[ -e /run/info.pid ]] правда, если файл существует | 
-f | 
[[ -f /tmp/test ]] правда, если существует и является файлом | 
-r | 
[[ -r ~/info ]] правда, если файл доступен на чтение | 
-w | 
[[ -w ~/result ]] правда, если файл доступен на запись | 
-x | 
[[ -x ~/bin/run ]] правда, если файл можно выполнять | 
while и untilПомимо итеративного for в Bash также есть бесконечные циклы while и utlit:
while list-1; do list-2; done
until list-1; do list-2; done
while будет выполнять свои действия пока условия в list-1 верныuntil будет выполнять свои действия пока условия в list-1 неверныВыйти из цикла можно с помощью break [n] Если передать число, тогда выход будет произведён из такого количества циклов
Для перехода к следующей итерации можно воспользоваться continue [n] Если передать число, то продолжение будет на соответствующем уровне вложенности
while#!/bin/bash
echo
while [ "$var1" != "end" ]
do
  echo "Input variable #1 (end to exit) "
  read var1                    # Not 'read $var1' (why?).
  echo "variable #1 = $var1"   # Need quotes because of "#"
  echo
done
exit 0Bash поддерживает следующие типы переменных:
Для декларирования переменной окружения используется команда export:
export [name=[word]] ...
В переменные $0, $1, $2 записываются аргументы скрипта
$0 содержит название скрипта$1 содержит первый аргумент и т.д.${10}$* и $@ позволяют обратиться сразу ко всем переменным$# позволяет получить количество переданных аргументовMINPARAMS=10
if [ $# -lt "$MINPARAMS" ]; then
  echo
  echo "This script needs at least $MINPARAMS arguments!"
fi
if [ -n "$1" ]              # Tested variable is quoted.
then
 echo "Parameter #1 is $1"  # Need quotes to escape #
fi
if [ -n "${10}" ]  # Parameters > $9 must be enclosed
then
 echo "Parameter #10 is ${10}"
fiКоманда shift производит “сдвиг” позиционных аргументов влево
$1 <--- $2, $2 <--- $3, $3 <--- $4, ...
Значение переменной $0 не изменяется
С помощью этой команды можно обойти все аргументы:
until [ -z "$1" ]  # Until all parameters used up . . .
do
  echo -n "$1 "
  shift
doneshift может сдвигать сразу на несколько позиций
Язык Bash имеет множество отличий в семантике работы от привычных языков программирования, поэтому надо быть осторожным с переносом своих привычек на язык Bash
Для проверки Bash-кода на наличие проблем рекомендуется использовать статический анализатор кода ShellCheck
Для установки его в GNU/Debian необходимо от имени сперпользователя
apt install shellcheck
#!/bin/bash
until [ -z $1 ]  # Until all parameters used up . . .
do
  echo -n "$1 "
  shift
done$ shellcheck test.sh
In test.sh line 3:
until [ -z $1 ]  # Until all parameters used up . . .
           ^-- SC2086: Double quote to prevent globbing and word splitting.
Did you mean:
until [ -z "$1" ]  # Until all parameters used up . . .
For more information:
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...