Bash краткое руководство

Если вы уже пользуетесь Linux, то наверняка ваши запросы к работе с компьютером шире, чем у пользователей Windows или macOS. Bash поможет вам эффективнее использовать Linux, автоматизировать рутинные задачи и в целом работать быстрее, чем позволяет графический интерфейс.

Если вы перешли на Linux недавно, то почитайте наш гайд по командам Bash. Здесь же мы будем разбираться в тонкостях языка и писать скрипт — это чуть более продвинутый уровень.

Содержание

  • Что такое Bash
  • Синтаксис Bash: из чего состоит
  • Переменные
  • Типы данных
  • Ветвления
  • Циклы
  • Функции
  • Пишем первый скрипт
  • Заключение

Bash (Bourne again shell) — это стандартная командная оболочка в большинстве дистрибутивов Linux и macOS, а также язык для этой оболочки.

Язык Bash поддерживает переменные, массивы, циклы, функции, условные и арифметические операторы, операции ввода-вывода и другие средства. Но главная его фишка — возможность создавать скрипты для ускорения работы на компьютере и автоматизации рутины. Например, с их помощью можно:

  • быстро переименовывать все файлы в папке;
  • генерировать случайные пароли;
  • создавать и отправлять отчёты по электронной почте;
  • проверять и обновлять систему и приложения;
  • одной командой очищать кэш браузера.

Последний скрипт из этого списка мы и попробуем написать в этой статье. Но для начала рассмотрим продвинутый синтаксис Bash и его основные конструкции: переменные, операторы, циклы и функции.

Кадр: мультфильм «Тачки» / Pixar

Кодить будем в терминале Linux — он открывается сочетанием клавиш Ctrl + Alt + T. Если вы хотите научиться работать с Bash, но пока не пользуетесь Linux, то пишите код в одном из онлайн-терминалов — например, Replit.

Переменные в Bash объявляются просто:

message="Hello, World!"

Теперь переменная message хранит значение — текстовое сообщение «Hello, World!». Давайте выведем его — сделать это можно с помощью команды echo:

echo $message # Будет выведено «Hello, World!»

Обратите внимание на отсутствие пробелов. В Bash пробелы являются разделителями. Если пробелы есть, то Bash будет считать их отдельными токенами и выдавать ошибки.

Следующий код приведёт к ошибке:

message = "Hello, World!"
bash: message: command not found

Также обратите внимание на символ $ в Bash он называется символом расширения (expansion character). Поставленный перед именем переменной, он указывает на то, что нужно использовать именно значение переменной, а не одноимённую строку.

Следующий код выведет сообщение «message»:

message="Hello, World!"
echo message

Чтобы сохранить в переменной введённое пользователем значение, используйте команду read:

read name
# Пользователь вводит что-то — например, «Olga»
echo $name # Будет выведено «Olga»

Если вы хотите добавить сообщение перед пользовательским вводом, добавьте флаг -p и сообщение после команды read:

read -p "Enter your name: " name # Будет выведено «Enter your name: »
# Пользователь вводит что-то — например, «Olga»
echo $name # Будет выведено «Olga»

В Bash есть несколько правил именования переменных:

  • Имя переменной должно начинаться с буквы латинского алфавита (нижнего или верхнего регистра) или символа подчёркивания _.
  • Оно может включать буквы, цифры и символ подчёркивания, но не может содержать пробелы или специальные символы.
  • Регистр букв в именах переменных имеет значение. Например, переменные var1 и Var1 будут рассматриваться как разные.
  • Не рекомендуется использовать зарезервированные слова Bash в качестве имён переменных.

Ну что же, вот мы и создали нашу первую переменную. Поздравляем! Пока эта переменная содержит всего один тип данных — строковый (string). Но в Bash он далеко не единственный — давайте разберём ещё несколько.

Важная оговорка

В Bash нет строгой системы типов, как в некоторых других языках. Тип данных зависит от содержимого переменной и контекста её использования. Поэтому термин «типы данных» в этой статье используется условно, для облегчения понимания. Простите нам это упрощение.

Строки (strings) — это набор символов, заключённый в кавычки. В Bash можно писать строки в одинарных ‘…’ или двойных «…» кавычках. Например, ‘Привет’ и «Мир» — это строки.

Числа (numbers) — это целочисленные значения. Bash поддерживает арифметические операции с целыми числами, включая сложение, вычитание, умножение и деление. Арифметические выражения нужно предварять знаком $ и обрамлять двойными круглыми скобками:

a=2 b=3
echo $(($a + $b)) # Будет выведено 5

Также можно использовать команду expr:

echo $(expr $a + $b)  # Будет выведено 5

Стандартные библиотеки Bash поддерживают следующие арифметические операции:

  • Сложение — +.
  • Вычитание — .
  • Умножение — *.
  • Целочисленное деление — /.
  • Остаток от деления — %.
  • Возведение в степень — **.

Примечание: арифметические операторы в Bash не работают с числами с плавающей точкой.

Массивы (arrays) — тип переменных, который может хранить несколько значений. Элементы массива перечисляются в круглых скобках через пробел:

my_array=(apple banana cherry)

При использовании переменной массива в команде применяются фигурные скобки. Если мы хотим вывести его отдельные элементы, это можно сделать с помощью символа $ и номера индекса в квадратных скобках:

echo ${my_array[0]} # Выведет «apple»
echo ${my_array[1]} # Выведет «banana»
echo ${my_array[2]} # Выведет «cherry»

А если нам нужно получить сразу все элементы массива, вместо индекса в квадратных скобках нужно поставить символ * или @:

echo ${my_array[*]} # Выведет «apple banana cherry»
echo ${my_array[@]} # Выведет «apple banana cherry»

Логические значения (boolean) — нужны для того, чтобы проверить, выполняется ли какое-то условие или нет. Они могут быть либо true (истина), либо false (ложь). Например, в операторах if и while мы пишем условие, и если оно true, то выполняем какое-то действие.

В Bash логические значения представлены целыми числами. Число 0 означает true, а любое другое число означает false. Когда мы выводим логическое значение на экран, то вместо true мы видим 0.

Также в Bash есть операторы сравнения. Например, эти нужны для строк:

  • = проверяет, одинаковы ли две строки.
  • != проверяет, различаются ли две строки.

А эти — для чисел:

  • -eq проверяет, равны ли два числа.
  • -ne проверяет, различаются ли два числа.
  • -gt проверяет, больше ли первое число, чем второе.
  • -lt проверяет, меньше ли первое число, чем второе.
  • -ge выводит true, если первое число больше или равно второму.
  • -le выводит true, если первое число меньше или равно второму.

Использовать операторы сравнения можно с помощью команды test. Команда test принимает два значения и оператор сравнения и возвращает 1 или 0 в зависимости от результата:

test "hello" = "hello" 
echo $? 
# Выведет 0 (true), так как строки одинаковые
test "hello" != "world" 
echo $? 
# Выведет 0 (true), так как строки различаются

Помимо команды test, операторы сравнения можно использовать квадратных скобках [ ]. Но для этого нам понадобится разобраться, как работать с ветвлениями.

Ветвления if-then-else позволяют проверить условие и выполнить один набор команд, если условие истинно, и другой набор команд, если условие ложно. Это выглядит так:

if [ условие ]; 
then
    # Команды, которые нужно выполнить, если условие истинно
else
    # Команды, которые нужно выполнить, если условие ложно
fi

Обратите внимание: открывающую и закрывающую квадратные скобки нужно отделять от операндов пробелами.

А если в паре с основным условием нужно проверить дополнительное, используют конструкцию elif:

if [ условие_1 ]; 
then
    # Команды, которые нужно выполнить, если условие_1 истинно
elif [ условие_2 ];
then
    # Команды, которые нужно выполнить, если условие_2 истинно
else
    # Команды, которые нужно выполнить, если все условия ложны
fi

Существует и более короткая форма записи условной конструкции — if-then. Её используют, когда нужно проверить только одно условие:

if [ условие ]; 
then 
# Команды, которые нужно выполнить
fi

В этом случае команды будут выполнены, только если условие истинно.

Для наглядности давайте напишем скрипт, который проверяет правильность введённого пароля:

#!/bin/bash
# Задаём значение переменной password
password="14pomTEr"

# Запрашиваем ввод пароля у пользователя и сохраняем полученное значение в переменную user_input
read -p "Enter the password: " user_input

# Сравниваем значение переменной password с введённым пользователем значением user_input
if [ "$password" = "$user_input" ]; then
  # Если пароли совпадают, выводим сообщение о том, что пароль введён правильно
  echo "Password accepted"
else
  # Если пароли не совпадают, выводим сообщение о том, что пароль введён неправильно
  echo "Wrong password"
fi

Кроме того, в Bash есть логические операторы & & (логическое И) и || (логическое ИЛИ), которые позволяют комбинировать несколько условий и возвращать соответствующее булево значение.

Например, вот как будет выглядеть наша программа, если мы добавим проверку логина:

#!/bin/bash

username="OlgaBelova94"  # Устанавливаем переменную с именем пользователя
password="14pomTEr"

read -p "Enter your username: " user_input_username  # Запрашиваем имя пользователя
read -p "Enter the password: " user_input_username  # Запрашиваем пароль

echo "Enter the password: "
read user_input_password  # Запрашиваем пароль

if [ "$username" == "$user_input_username" ] && [ "$password" == "$user_input_password" ]; then
  echo "Password accepted"
else
  echo "Wrong username or password"  # Уточняем, что именно было неверно
fi

Здесь мы получим значение true и сообщение «Password accepted», только если будут выполнены оба условия.

Как и в большинстве языков программирования, в Bash есть циклы. Они бывают двух видов:

  • for — когда количество итераций заранее известно.
  • while — когда количество итераций заранее не известно.

Вот как выглядит сигнатура цикла for:

for переменная цикла in элемент1 элемент2 ... элементN
do
# Команды, которые будут выполняться в цикле
done

Количество итераций (часть между in и do) можно указать простым перечислением элементов:

for fruit in apple banana cherry
do
    echo "I like $fruit"
done

# Будет выведено:
# I like apple
# I like banana
# I like cherry

Можно перебрать элементы массива:

my_array=("apple" "banana" "orange")

for fruit in "${my_array[@]}"
do
    echo "I like $fruit"
done

# Будет выведено:
# I like apple
# I like banana
# I like cherry

Для явного указания нужного количества итераций используют арифметические выражения:

for ((i=0; i<5; i++))
do
  echo "Iteration number $i"
done
# Будет выведено:
# Iteration number 0
# Iteration number 1
# Iteration number 2
# Iteration number 3
# Iteration number 4

А вот так выглядит сигнатура цикла while:

while [ условие ]
do
  # Команды, которые будут выполняться в цикле
done

Условие (condition) — это выражение, результат которого является логическим значением true или false. Команды внутри цикла будут выполняться до тех пор, пока condition возвращает true.

Давайте усовершенствуем нашу программу проверки пароля так, чтобы она запрашивала его до тех пор, пока пользователь не введёт верный:

#!/bin/bash

password="14pomTEr"
user_input=""

while [ "$user_input" != "$password" ]
do
  read -p "Enter the password: " user_input
  echo "Wrong password, login denied"
done

echo "Password accepted, login allowed"

Что здесь происходит. В этом примере цикл while запрашивает пароль у пользователя, а потом проверяет его на совпадение с правильным с помощью оператора [«$user_input» != «$password»]. А дальше происходит следующее:

  • Если пароль неверный, программа выводит соответствующее сообщение, а цикл продолжается.
  • Если пароль верный, программа выводит сообщение о принятии пароля и завершает выполнение.

Функции на языке Bash имеют такой вид:

имя_функции (аргументы) {
    # Тело функции
}

Например, вот как выглядит простая функция без аргументов:

say_hello () {       # Создаём функцию

    echo hello
}

say_hello        # Вызываем функцию, будет выведено «hello»

А вот как выглядит функция, которая принимает аргументы:

repeat_text () {
    local text=$1
    local reps=$2

    for (( i=0; i<$reps; i++ )); do
        echo "$text"
    done
}
repeat_text "hello" 3

Что здесь происходит:

  • С помощью ключевого слова local мы создали две локальные переменные — то есть переменные, существующие только внутри функции.
  • В одну из них поместили текст, а во вторую — количество повторов.
  • Затем создали цикл, который берёт текст из первой переменной и повторяет его столько раз, сколько раз указано во второй.
  • Вызвали функцию, передав ей значения hello и 3 — то есть приказали повторить слово «hello» три раза.

Настало время применить знания на практике — напишем скрипт для автоматической очистки кэша браузера и DNS.

Состоять наш скрипт будет из трёх функций: первая — для очистки кэша браузера, вторая — для очистки кэша DNS, а в третью завернём небольшое меню для выбора нужного действия.

Начинаем, как водится, с шебанга — специальной строки, которая говорит компьютеру, с помощью какой программы нужно запускать скрипт. Так как мы хотим использовать bash, которая лежит в папке /bin, шебанг будет выглядеть так:

#!/bin/bash

Теперь напишем функцию, которая будет очищать браузерный кэш. В комментариях — разбор синтаксиса.

clear_browser_cache() {

# Проверяем, какой браузер установлен на компьютере
    if command -v google-chrome >/dev/null 2>&1; then

# Если установлен Google Chrome, очищаем кэш браузера
        google-chrome --user-data-dir=$(mktemp -d) --disable-extensions --disable-plugins --disable-geolocation --no-default-browser-check --no-first-run --noerrdialogs --disable-session-crashed-bubble --disable-infobars --disk-cache-size=0 --media-cache-size=0

# Далее проверяем, установлен ли Firefox
    elif command -v firefox >/dev/null 2>&1; then

# Если установлен Firefox, очищаем кэш браузера
        firefox --new-instance --safe-mode

# Если браузер не установлен, выводим сообщение об ошибке
    else
        echo "Error: no supported browser found"
    fi
}

Далее напишем функцию для очистки кэша DNS.

clear_dns_cache() {
# Проверяем, какая операционная система установлена на компьютере
# Если установлена Linux, очищаем кэш DNS
    if [[ "$OSTYPE" == "linux-gnu"* ]]; then
        sudo systemd-resolve --flush-caches
# Если установлена macOS, очищаем кэш DNS
    elif [[ "$OSTYPE" == "darwin"* ]]; then
        sudo dscacheutil -flushcache
        sudo killall -HUP mDNSResponder
# Если операционная система не поддерживается, выводим сообщение об ошибке
    else
        echo "Error: unsupported operating system"
    fi
}

Ну а теперь дело за малым. Напишем небольшое меню, чтобы пользователь мог выбрать, кэш браузера или кэш DNS нужно очистить.

# Сначала выведем варианты
clear_cache() {
    echo "Select cache to clear:"
    echo "1. Browser cache"
    echo "2. DNS cache"
# Запросим у пользователя номер выбранного пункта меню
    read -p "Enter your choice: " choice
# В зависимости от выбранного пункта меню вызываем соответствующую функцию для очистки кэша
    if [ "$choice" -eq 1 ]; then
    clear_browser_cache
elif [ "$choice" -eq 2 ]; then
    clear_dns_cache
else
    echo "Invalid choice"
fi
}

Вуаля! Наш скрипт готов. Чтобы его запустить, нам нужно лишь вызвать функцию clear_cache:

clear_cache

В теории можно сделать скрипт и ещё лучше — например, добавить больше браузеров, реализовать проверку прав администратора и другие фишки. Но на сегодня, пожалуй, достаточно.

Вот вы и освоили основные элементы языка Bash и даже написали свой первый скрипт — поздравляем! Если хотите потренироваться в решении задачек на Bash, можете заглянуть в этот онлайн-тренажёр — он бесплатный и довольно гуманный по отношению к новичкам.

Ещё можно посмотреть на проект TryBash, где вся базовая теория дана в виде коротких интерактивных уроков. Там же можно забрать удобную шпаргалку с основными командами Bash, чтобы постоянно не бегать в Google во время обучения и экспериментов.

А если чувствуете, что хотите знать больше, можете почитать Advanced Bash-Scripting Guide («Расширенный гайд по написанию скриптов на Bash») — он на английском, но есть хороший любительский перевод. Ну, или приходите на курс Skillbox «Администрирование ОС Linux» — там тоже много всего полезного :)

Время на прочтение
5 мин

Количество просмотров 1.3M

Безусловно, все те кто общается с ОС Linux хоть раз да имели дело(во всяком случае слышали точно) с командной оболочкой BASH. Но BASH не только командная оболочка, это еще и превосходный скриптовый язык программирования.
Цель этой статьи — познакомить поближе юзеров с bash, рассказать про синтаксис, основные приемы и фишки языка, для того чтобы даже обычный пользователь смог быстренько написать простой скрипт для выполнения ежедневной(-недельной, -месячной) рутинной работы или, скажем, «на коленке» наваять скриптик для бэкапа директории.

Введение

BASH

— Bourne-Again SHell (что может переводится как «перерожденный шел», или «Снова шел Борна(создатель sh)»), самый популярный командный интерпретатор в юниксоподобных системах, в особенности в GNU/Linux. Ниже приведу ряд встроенных команд, которые мы будем использовать для создания своих скриптов.

break выход из цикла for, while или until
continue выполнение следующей итерации цикла for, while или until
echo вывод аргументов, разделенных пробелами, на стандартное устройство вывода
exit выход из оболочки
export отмечает аргументы как переменные для передачи в дочерние процессы в среде
hash запоминает полные имена путей команд, указанных в качестве аргументов, чтобы не искать их при следующем обращении
kill посылает сигнал завершения процессу
pwd выводит текущий рабочий каталог
read читает строку из ввода оболочки и использует ее для присвоения значений указанным переменным.\
return заставляет функцию оболочки выйти с указанным значением
shift перемещает позиционные параметры налево
test вычисляет условное выражение
times выводит имя пользователя и системное время, использованное оболочкой и ее потомками
trap указывает команды, которые должны выполняться при получении оболочкой сигнала
unset вызывает уничтожение переменных оболочки
wait ждет выхода из дочернего процесса и сообщает выходное состояние.

И конечно же кроме встроенных команд мы будем использовать целую кучу внешних, отдельных команд-программ, с которыми мы познакомимся уже в процессе

Что необходимо знать с самого начала

1. Любой bash-скрипт должен начинаться со строки:

#!/bin/bash
в этой строке после #! указывается путь к bash-интерпретатору, поэтому если он у вас установлен в другом месте(где, вы можете узнать набрав whereis bash) поменяйте её на ваш путь.
2. Коментарии начинаются с символа # (кроме первой строки).
3. В bash переменные не имеют типа(о них речь пойдет ниже)

Переменные и параметры скрипта

Приведу как пример небольшой пример, который мы разберем:

#!/bin/bash
#указываем где у нас хранится bash-интерпретатор
parametr1=$1 #присваиваем переменной parametr1 значение первого параметра скрипта
script_name=$0 #присваиваем переменной script_name значение имени скрипта
echo "Вы запустили скрипт с именем $script_name и параметром $parametr1" # команда echo выводит определенную строку, обращение к переменным осуществляется через $имя_переменной.
echo 'Вы запустили скрипт с именем $script_name и параметром $parametr1' # здесь мы видим другие кавычки, разница в том, что в одинарных кавычках не происходит подстановки переменных.
exit 0 #Выход с кодом 0 (удачное завершение работы скрипта)

Результат выполнения скрипта:

ite@ite-desktop:~$ ./test.sh qwerty
Вы запустили скрипт с именем ./test.sh и параметром qwerty
Вы запустили скрипт с именем $script_name и параметром $parametr1

После того как мы познакомились как использовать переменные и передавать скрипту параметры, время познакомиться с зарезервированными переменными:

$DIRSTACK - содержимое вершины стека каталогов
$EDITOR - текстовый редактор по умолчанию
$EUID - Эффективный UID. Если вы использовали программу su для выполнения команд от другого пользователя, то эта переменная содержит UID этого пользователя, в то время как...
$UID - ...содержит реальный идентификатор, который устанавливается только при логине.
$FUNCNAME - имя текущей функции в скрипте.
$GROUPS - массив групп к которым принадлежит текущий пользователь
$HOME - домашний каталог пользователя
$HOSTNAME - ваш hostname
$HOSTTYPE - архитектура машины.
$LC_CTYPE - внутренняя переменная, котороя определяет кодировку символов
$OLDPWD - прежний рабочий каталог
$OSTYPE - тип ОС
$PATH - путь поиска программ
$PPID - идентификатор родительского процесса
$SECONDS - время работы скрипта(в сек.)
$# - общее количество параметров переданных скрипту
$* - все аргументы переданыне скрипту(выводятся в строку)
$@ - тоже самое, что и предыдущий, но параметры выводятся в столбик
$! - PID последнего запущенного в фоне процесса
$$ - PID самого скрипта

Условия

Условные операторы, думаю, знакомы практически каждому, кто хоть раз пытался на чем-то писать программы. В bash условия пишутся след. образом (как обычно на примере):
#!/bin/bash
source=$1 #в переменную source засовываем первый параметр скрипта
dest=$2 #в переменную dest засовываем второй параметр скрипта

if [[ "$source" -eq "$dest" ]] # в ковычках указываем имена переменных для сравнения. -eq - логическое сравнение обозначающие "равны"
then # если они действительно равны, то
echo "Применик $dest и источник $source один и тот же файл!" #выводим сообщение об ошибке, т.к. $source и $dest у нас равны
exit 1 # выходим с ошибкой (1 - код ошибки)
else # если же они не равны
cp $source $dest # то выполняем команду cp: копируем источник в приемник
echo "Удачное копирование!"
fi #обозначаем окончание условия.


Результат выполнения скрипта:
ite@ite-desktop:~$ ./primer2.sh 1 1
Применик 1 и источник 1 один и тот же файл!
ite@ite-desktop:~$ ./primer2.sh 1 2
Удачное копирование!

Структура if-then-else используется следующим образом:
if <команда или набор команд возвращающих код возврата(0 или 1)>
then
<если выражение после if истино, то выполняется этот блок>
else
<если выражение после if ложно, тот этот>
В качестве команд возвращающих код возврата могут выступать структуры [[ , [ , test, (( )) или любая другая(или несколько) linux-команда.
test - используется для логического сравнения. после выражения, неоьбходима закрывающая скобка "]"
[ - синоним команды test
[[ - расширенная версия "[" (начиная с версии 2.02)(как в примере), внутри которой могут быть использованы || (или), & (и). Долна иметь закрывающуб скобку "]]"
(( )) - математическое сравнение.
для построения многоярусных условий вида:
if ...
then ....
else
if ....
then....
else ....

для краткости и читаемости кода, можно использовать структуру:
if ..
then ...
elif ...
then ...
elif ...

Условия. Множественный выбор

Если необходимо сравнивать какоую-то одну переменную с большим количеством параметров, то целесообразней использовать оператор case.
#!/bin/bash
echo "Выберите редатор для запуска:"
echo "1 Запуск программы nano"
echo "2 Запуск программы vi"
echo "3 Запуск программы emacs"
echo "4 Выход"
read doing #здесь мы читаем в переменную $doing со стандартного ввода

case $doing in
1)
/usr/bin/nano # если $doing содержит 1, то запустить nano
;;
2)
/usr/bin/vi # если $doing содержит 2, то запустить vi
;;
3)
/usr/bin/emacs # если $doing содержит 3, то запустить emacs
;;
4)
exit 0
;;
*) #если введено с клавиатуры то, что в case не описывается, выполнять следующее:
echo "Введено неправильное действие"

esac #окончание оператора case.


Результат работы:
ite@ite-desktop:~$ ./menu2.sh
Выберите редатор для запуска:
1 Запуск программы nano
2 Запуск программы vi
3 Запуск программы emacs
4 Выход

После выбор цифры и нажатия Enter запуститься тот редактор, который вы выбрали(если конечно все пути указаны правильно, и у вас установлены эти редакторы :) )
Прведу список логических операторв, которые используются для конструкции if-then-else-fi:
-z # строка пуста
-n # строка не пуста
=, (==) # строки равны
!= # строки неравны
-eq # равно
-ne # неравно
-lt,(< ) # меньше
-le,(<=) # меньше или равно
-gt,(>) #больше
-ge,(>=) #больше или равно
! #отрицание логического выражения
-a,(&&) #логическое «И»
-o,(||) # логическое «ИЛИ»

С основами языка и условиями мы разобрались, чтобы не перегружать статью, разобью её на несколько частей(допустим на 3). Во второй части разберем операторы цикла и выполнение математических операций.

UPD: Исправил некоторые ошибки
UPD: Обновил часть про условия if-then-else

Статьи на unix-admin.su

Вы когда-нибудь замечали, как супер-ботаник хакер в кино может легко проникнуть в самые безопасные банки и ограбить их все, просто набрав несколько команд, яростно уставившись в зеленый экран с черным фоном? Как человек последовательно получает доступ ко всем паролям и берет под контроль скрытые камеры, где бы он ни находился, всего несколькими нажатиями клавиш на клавиатуре. Ну, я не уверен, откуда у кинематографистов это взялось, но, наверное, это их способ сказать нам, что командная строка — это мощный инструмент, и без всех этих хакеров и ACCESS GRANTED!! До смешного..

Часто новички настолько привыкли работать с графическим интерфейсом GUI, что склонны игнорировать возможности интерфейса командной строки (CLI). Мышь очень удобна, когда нужно скопировать в папку около ста тысяч файлов, но что, если переименовать все эти тысячи файлов или разделить их по расширениям? Так как графические интерфейсы не программируемы, то переименование или разделение их с помощью командной строки заняло бы целую вечность, однако, мы могли бы быстро добиться этого за несколько строк кода.

Unix shell является довольно мощным инструментом для разработчиков всех видов. Эта статья призвана дать краткое введение в самые основы, начиная с операционной системы UNIX.

UNIX

Большинство современных операционных систем, за исключением WINDOWS, построены на базе UNIX. Среди них много дистрибутивов Linux, macOS, iOS, Android и др. Достаточно одного взгляда на схему развития операционных систем, основанных на UNIX, чтобы подчеркнуть важность UNIX, и именно по этой причине она стала широко использоваться на производстве. На самом деле, бэк-энд многих вычислительных систем, включая такие промышленные гиганты, как Facebook и Google, в значительной степени используют UNIX.

Shell

Shell — это интерфейс командной строки для запуска программ на компьютере. Пользователь набирает в строке запроса кучу команд, оболочка запускает программы для пользователя, а затем выводит результат. Команды могут быть либо введены непосредственно пользователем, либо прочитаны из файла, называемого shell-скриптом или shell-программой.

Shell. Какие бывают

UNIX-система обычно предлагает различные типы оболочек shell. Некоторые из них наиболее распространены:

Однако в этой статье мы ограничимся оболочкой Bash. Тем не менее, вам рекомендуется прочитать и попробовать другие оболочки, особенно zsh, так как уже в версии MacOS под названием Catalina zsh заменил оболочку bash. Так что будет хорошей идеей познакомиться с ним сейчас.

Terminal

Терминал — это программа, которая используется для взаимодействия с оболочкой. Это просто интерфейс к оболочке и к другим программам командной строки, которые запускаются внутри нее. Это похоже на то, как веб-браузер является интерфейсом для веб-сайтов. Вот как выглядит типичный терминал в Mac:

У Mac и Linux есть соответствующие версии терминала. Windows также имеет встроенную командную оболочку, но она основана на командной строке MS-DOS, а не на UNIX. Так что давайте посмотрим, как мы можем установить шелл и программу терминала в операционной системе Windows, которая работает так же, как и в системах Mac и Linux.

Установка терминала в WINDOWS

  • Подсистема Windows для Linux (WSL)

Это новая система совместимости с Linux в Windows 10. WSL позволяет разработчикам запускать GNU/Linux окружение — включая большинство инструментов командной строки, утилит и приложений — прямо под Windows, без изменений, без дополнительных виртуальных машин. Подробнее о его установке и функциях вы можете прочитать здесь.

  • Git Bash

Git Bash — это то, что мы будем использовать в этой статье. Скачайте Git на компьютер с Windows отсюда и установите его со всеми настройками по умолчанию. В конце концов, вы получите окно терминала, вроде того, что показано ниже.

Изучение терминала

Всякий раз, когда мы открываем окно терминала, мы видим наши последние учетные данные для входа и приглашение Shell. Строка shell появляется всякий раз, когда оболочка готова принять входные данные. Она может немного отличаться в зависимости от дистрибутива, но в основном она отображается как имяпользователя@имякомпьютера, за которым следует знак $.

Если вам не нужна вся эта информация, вы можете использовать PS1 для настройки командной строки.

Теперь терминал будет показывать только $ в строке. Однако это только временно и после перезапуска терминал вернется к своим первоначальным настройкам.

Приступим

Чтобы немного развлечься, давайте начнем с нескольких простых команд:

  • echo: выводит все, что вы набираете в командной строке шелла, аналогично Print на Python.

  • date: отображает текущие время и дату.

  • cal: отображает календарь на текущий месяц.

  • clear: очистит окно терминала, также можно использовать сочетание клавиш Ctrl-L.

Команда bash — наименьшая единица кода, которую bash может выполнить самостоятельно. Эти команды говорят bash, что нам нужно, чтобы он сделал. Обычно bash принимает от пользователя одну команду и возвращается к пользователю после ее выполнения.

Рабочая директория

pwd

pwd выводит содержимое рабочей директории и указывает на текущую рабочую директорию, то есть на директорию, которую оболочка просматривает в данный момент. Это также место по умолчанию, где команды оболочки будут искать файлы данных.
Директория похожа на папку, но в shell мы будем придерживаться наименования директория (каталог). Файловая иерархия UNIX имеет древовидную структуру. Чтобы добраться до определенной папки или файла, нам нужно пройти по определенным путям внутри этой древовидной структуры. Пути разделяют каждый узел вышеуказанной структуры с помощью символа слэш( / ).

Навигация по директориям

Для навигации и организации файлов используются команды ls и cd.

ls

ls обозначает список и показывает содержимое директории. ls обычно начинает с просмотра нашего домашнего каталога. Это означает, что если мы вызовем команду ls самостоятельно, то он всегда выведет содержимое текущего каталога, которым в моем случае является /Users/parul.

Параметры команды

Параметры и опции включают некоторые специальные функции при использовании команды ls.

  • ls <папка> : для просмотра содержимого определенной папки.
  • ls -a: Для перечисления всех скрытых файлов в папке
  • ls -l: Выводитболее длинный и подробный список файлов. ls -l также может быть использован с именем каталога для перечисления файлов этого конкретного каталога.
  • ls ~: tilde(~) — сокращение, обозначающее домашний каталог. Таким образом, независимо от того, в какой директории мы находимся, ls ~ всегда будет показывать содержимое домашнего каталога.

Немного о масках файлов

Оболочка также позволяет нам сопоставлять имена файлов с шаблонами, обозначенными звездочкой(*). Она служит подстановочным знаком для замены любого другого символа внутри заданного шаблона. Например, если мы введем *.txt, то это приведет к перечислению всех файлов с расширением .txt. Давайте попробуем перечислить все файлы с расширением .py в нашей папке Demo:

cd

cd, дословно, означает Change Directory (Изменить директорию) и изменяет активную директорию на указанный путь. После того, как мы используем cd в нужную директорию, можно воспользоваться командой ls, чтобы увидеть содержимое ее содержимое. Давайте посмотрим, как можно использовать эту команду:

  • cd … : Чтобы вернуться в родительский каталог.
  • cd : Для возврата в домашний каталог

Организация файлов

Существуют определенные команды, которые позволяют нам перемещать, удалять, создавать и копировать файлы из самой оболочки.

mkdir

mkdir означает Make directory и используется для создания новой директории или папки.

mv

mv означает Move и перемещает один или несколько файлов или каталогов из одного места в другое. Нам нужно указать, что мы хотим переместить, т.е. источник и куда мы хотим переместить их, т.е. место назначения.

Создадим в папке Demo новую директорию PythonFiles и переместим в нее все .py-файлы из папки Demo, используя две вышеприведенные команды.

touch

Команда touch используется для создания новых пустых файлов. Она также используется для изменения временных меток на существующих файлах и каталогах. Вот как мы можем создать файл под названием foo.txt в папке Demo.

rm

Команда rm означает «Удалить» и удаляет файлы или каталоги. По умолчанию она не удаляет каталоги, но если используется как rm -r * внутри каталога, то удаляется каждый каталог и файл внутри этого каталога.
Теперь удалим ранее созданный файл foo.txt.

rmdir

rmdir означает «удалить каталог» и используется для удаления пустых каталогов из файловой системы. Давайте удалим папку PythonFiles, которую мы создали некоторое время назад.

Обратите внимание, что ../ обозначает родительский каталог.

Просмотр файлов

Это еще один нюанс использования оболочки shell, который очень полезен. Существуют команды, которые помогают нам просматривать содержимое файла, чтобы мы могли ими манипулировать.

cat

Команда cat читает файл и выводит его содержимое. Она может читать любое количество файлов, как следует из ее наименования. В нашей папке Demo есть несколько текстовых файлов, давайте используем cat, чтобы просмотреть их содержимое.

Чтобы просмотреть более одного файла, укажите оба имени файлов после команды cat:

$ cat Names.txt fruits.txt

less

Команда cat отображает содержимое файла на экране. Это нормально, когда содержимого мало, но это становится проблемой, когда файл большой. Как видно из примера ниже, команда выводит все на терминал с очень высокой скоростью, и мы не можем разобраться во всем содержимом файла. К счастью, есть команда под названием «Меньше», которая позволяет нам просматривать содержимое по одному экрану за раз.

$ less babynames.txt

Существуют определенные варианты с меньшим количеством используемых опций:

Пробел: перейти к следующему экрану
b: перейти к предыдущему экрану
/: для поиска конкретного слова
q: выйти

man

Команда man отображает страницы мануалов, которые являются руководством пользователя, встроенным по умолчанию во многие Linux и большинство Unix операционных систем.

man bash : Чтобы отобразить всю инструкцию bash

man <ключевое слово>, например man ls выдает информацию о команде ls.

Каналы и фильтры

Оператор pipe ‘|’ (вертикальная полоса) — это способ отправки вывода одной команды в качестве входной для другой команды.

command1 | command2

Когда команда посылает свой выход в канал, принимающей стороной для этого выхода является другая команда, а не файл. На рисунке ниже показано, как команда wc подсчитывает содержимое файла, отображенного командой cat.

wc — это команда, которая принимает данные на входе и каким-то образом преобразует их вывод. Такие команды называются фильтрами и помещаются после Unix каналов.

Фильтры

Теперь давайте посмотрим на некоторые часто используемые команды фильтров. Мы будем работать с файлом под названием babynames.txt, который содержит около 1000 имен детей, и с файлом fruits.txt, который содержит имена нескольких фруктов.

  • grep или глобальное регулярное выражение ищет строки с заданной строкой или ищет шаблон в заданном входном потоке и выводит результат. Следующая команда прочитает все файлы и выведет все строки, содержащие либо слово ‘Tom’.

Но получился большой список, и мы не можем разобраться во всех этих данных, только что выведенных в терминале. Давайте посмотрим, как мы можем использовать оператор pipe, чтобы разобраться в этом.

  • Фильтр wc — это сокращение от количества слов (word count). Он читает список файлов и генерирует одну или несколько следующих статистик: количество новых строк, количество слов и количество байтов. Введем в wc вывод вышеприведенной команды grep для подсчета количества строк, содержащих слово ‘Tom’.

  • Фильтр sort сортирует строки в алфавитном или цифровом порядке.

Команда cat сначала читает содержимое файла fruits.txt, а затем сортирует его.

  • Фильтр uniq означает «уникальный» и дает нам количество уникальных строк во входном потоке.

Важно отметить, что uniq не может обнаружить дубликаты записей, если они не находятся рядом. Поэтому мы использовали сортировку файла перед использованием команды сортировки. В качестве альтернативы можно также использовать команду sort -u вместо uniq.

Каналы очень удобны для выполнения некоторых сложных задач, так как несколько команд могут быть объединены в один канал вывода.

Что изучить еще по теме?

Инструменты командной строки могут быть отличным дополнением к основному набору инструментов. Первоначально терминал может напугать новичков, но как только они его освоят, его реальные достоинства и преимущества могут быть реализованы. Данная статья призвана заставить начинающего пользователя начать изучение терминала только поверхностно. В сети доступно множество полезных ресурсов, позволяющих детально изучить и узнать об оболочке. Вот некоторые из рекомендуемых ресурсов (на английском):

  • Командная строка Linux пятое интернет-издание William Shotts
  • Bash — Руководство для начинающих
  • The Bash Academy

The Ultimate Linux Command Line Guide - Full Bash Tutorial

Welcome to our ultimate guide to the Linux Command Line. This tutorial will show you some of the key Linux command line technologies and introduce you to the Bash scripting language.

What is Bash?

Bash (short for Bourne Again SHell) is a Unix shell, and a command language interpreter. A shell is simply a macro processor that executes commands. It’s the most widely used shell packaged by default for most Linux distributions, and a successor for the Korn shell (ksh) and the C shell (csh).

Many things that can be done Linux operating system can be done via command line. Some examples are…

  • Editing files
  • Adjusting the volume of the operating system
  • Fetching web pages from the internet
  • Automating work you do every day

You can read more about bash here, via the GNU Documentation, and via the tldp guide.

Using bash on the command line (Linux, OS X)

You can start using bash on most Linux and OS X operating systems by opening up a terminal. Let’s consider a simple hello world example. Open up your terminal, and write the following line (everything after the $ sign):

zach@marigold:~$ echo "Hello world!"
Hello world!

As you can see, we used the echo command to print the string “Hello world!” to the terminal.

Writing a bash script

You can also put all of your bash commands into a .sh file, and run them from the command line. Say you had a bash script with the following contents:

#!/bin/bash
echo "Hello world!"

It’s worth noting that first line of the script starts with #!. It is a special directive which Unix treats differently.

Why did we use #!/bin/bash at the beginning of the script file?

That is because it is a convention to let the interactive shell know what kind of interpreter to run for the program that follows. The first line tells Unix that the file is to be executed by /bin/bash. This is the standard location of the Bourne shell on just about every Unix system. Adding #!/bin/bash as the first line of your script, tells the OS to invoke the specified shell to execute the commands that follow in the script. #! is often referred to as a “hash-bang”, “she-bang” or “sha-bang”. Though it is only executed if you run your script as an executable. For example, when you type ./scriptname.extension, it will look at the top line to find out the interpreter, whereas, running the script as bash scriptname.sh, first line is ignored.

Then you could run the script like so: For make file executable you should call this command under sudo chmod +x “filename”.

zach@marigold:~$ ./myBashScript.sh
Hello world!

The script only has two lines. The first indicates what interpreter to use to run the file (in this case, bash). The second line is the command we want to use, echo, followed by what we want to print which is “Hello World”.

Sometimes the script won’t be executed, and the above command will return an error. It is due to the permissions set on the file. To avoid that use:

zach@marigold:~$ chmod u+x myBashScript.sh

And then execute the script.

Cat is one of the most frequently used commands in Unix operating systems.

Cat is used to read a file sequentially and print it to the standard output. The name is derived from its function to concatenate files.

Usage

cat [options] [file_names]

Most used options:

  • -b, numer non-blank output lines
  • -n, number all output lines
  • -s, squeeze multiple adjacent blank lines
  • -v, display nonprinting characters, except for tabs and the end of line character

Example

Print in terminal the content of file.txt:

cat file.txt

Concatenate the content of the two files and display the result in terminal:

cat file1.txt file2.txt

Linux Command Line: Bash cd

Change Directory to the path specified, for example cd projects.

There are a few really helpful arguments to aid this:

  • . refers to the current directory, such as ./projects
  • .. can be used to move up one folder, use cd .., and can be combined to move up multiple levels ../../my_folder
  • / is the root of your system to reach core folders, such as system, users, etc.
  • ~ is the home directory, usually the path /users/username. Move back to folders referenced relative to this path by including it at the start of your path, for example ~/projects.

Linux Command Line: Bash head

Head is used to print the first ten lines (by default) or any other amount specified of a file or files. Cat is used to read a file sequentially and print it to the standard output.
ie prints out the entire contents of the entire file. — that is not always necessary, perhaps you just want to check the contents of a file to see if it is the correct one, or check that it is indeed not empty. The head command allows you to view the first N lines of a file.

if more than on file is called then the first ten lines of each file is displayed, unless specific number of lines are specified. Choosing to display the file header is optional using the option below

Usage

head [options] [file_name(s)]

Most used options:

  • -n N, prints out the first N lines of the file(s)
  • -q, doesn’t print out the file headers
  • -v, always prints out the file headers

Example

head file.txt

Prints in terminal the first ten lines of file.txt (default)

head -n 7 file.txt

Prints in terminal the first seven lines of file.txt

head -q -n 5 file1.txt file2.txt

Print in terminal the first 5 lines of file1.txt, followed by the first 5 lines of file2.txt

Linux Command Line: Bash ls

ls is a command on Unix-like operating systems to list contents of a directory, for example folder and file names.

Usage

cat [options] [file_names]

Most used options:

  • -a, all files and folders, including ones that are hidden and start with a .
  • -l, List in long format
  • -G, enable colorized output.

Example:

List files in freeCodeCamp/guide/

ls                                                                ⚬ master
CODE_OF_CONDUCT.md bin                package.json       utils
CONTRIBUTING.md    gatsby-browser.js  plugins            yarn.lock
LICENSE.md         gatsby-config.js   src
README.md          gatsby-node.js     static
assets             gatsby-ssr.js      translations

Linux Command Line: Bash man

Man, the abbreviation of manual, is a bash command used to display on-line reference manuals of the given command.

Man displays the reletive man page (short for manual page) of the given command.

Usage

man [options] [command]

Most used options:

  • -f, print a short description of the given command
  • -a, display, in succession, all of the available intro manual pages contained within the manual

Example

Display the man page of ls:

man ls

Linux Command Line: Bash mv

Moves files and folders.

mv source target
mv source ... directory

The first argument is the file you want to move, and the second is the location to move it to.

Commonly used options:

  • -f to force move them and overwrite files without checking with the user.
  • -i to prompt confirmation before overwriting files.

That’s all. Go forth and use Linux.


Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started

Перевели статью, которая поможет лучше понять, как работает bash, а значит, сэкономить время и уменьшить количество ошибок. Далее текст от лица автора.

Недавно я проводила рефакторинг установки для разнообразного набора приложений. Это была большая куча образов Docker, которые должны быть гибкими, но стабильными. Некоторые веб-приложения также необходимо перезапускать удобным для пользователя способом, который отображает полезные сообщения об ошибках для разработчиков различной квалификации.

Эта задача потребовала большой работы, но я уверена, что стала лучше разбираться в сценариях bash. Мне легко писать эту статью, потому что я записывала все странные мелочи, которые требовали времени на отладку.

Bash настолько странный, что не все из этих мелочей можно подробно разобрать в одной статье, поэтому в каждом разделе я даю ссылки на статьи или учебные пособия, где все эти моменты подробно расписаны.

Язык программирования, на котором писала ваша бабушка

Bash создан Брайаном Фоксом (легендарный и недооцененный парень) и выпущен в 1989 году как open source замена для Bourne Shell, вышедшей в 1976 году. Его название является аббревиатурой от Bourne Again SHell.

Если вы привыкли писать на любом другом языке программирования, bash (и сценарии оболочки в целом) не будут для вас интуитивно понятными. Синтаксис не запоминающийся, переменные странные, область видимости — полная дичь, и поток управления никогда не делает то, что вы думаете.

Как и в случае с CSS, я перестала бояться писать сценарии bash, когда узнала о нем несколько ключевых моментов: как он работает, в чем действительно хорош и как заставить его делать то, что мне нужно. Я также столкнулась с множеством глупых маленьких ошибок, которые мне просто пришлось заучить.

Написание скриптов командной оболочки — это очень весело, если вы овладеете основами! Ничто не заставит вас почувствовать себя более опытным хакером, чем написание дикого однострочника, который запускается с первой попытки.

Примечание: я предполагаю, что вы обладаете некоторыми предварительными знаниями в программировании и сценариях командной оболочки. Если вы только начинаете изучение — вот хороший ресурс для начала. Я предполагаю, что вы, по крайней мере, знаете, как использовать терминал и следующие команды: ls, cd, pwd, cat, grep и написали (или попытались написать) один или два сценария.

Кстати, поскольку эта статья относится к миру Linux и операционных систем, у меня есть примечание для тех, кто занимается этими вопросами дольше меня: нормально (даже рекомендуется!) исправлять меня, если я ошибаюсь, просто будьте вежливы.

Версии

Язык сценариев оболочки, c которым большинство из нас работает в настоящее время, — это версия bash для Mac и Linux, используемая для эмуляции терминала в /bin/bash.

Debian (и, соответственно, Ubuntu и Linux Mint) теперь использует другой, но в основном совместимый язык сценариев оболочки (dash) для системных задач. Прим. переводчика: так утверждает автор статьи, но я везде вижу использование bash.

Вы также можете установить zsh в качестве основной оболочки, который в целом похож на bash, но имеет и отличия.

Из-за всех этих небольших вариаций хорошей идеей будет поместить #!/bin/bash (или какой-либо другой язык сценариев оболочки, который вы хотите использовать) вверху файлов, чтобы указать, что сценарий оболочки должен использовать конкретно этот язык, а не какой-либо еще из установленных на сервере.

Указание языка оболочки в заголовке файла сделает его поведение более предсказуемым. Например, ответы на Stack Overflow обычно предполагают, что вы используете именно bash.

Основы

Давайте сначала рассмотрим несколько фундаментальных вещей.

Сценарии оболочки, по сути, представляют собой текстовые потоки, которые передают данные между собой. Они используют философию Unix, заключающуюся в том, чтобы хорошо выполнять одну задачу и объединять крошечные инструменты в более крупные программы по принципу работы конвейера в промышленности. Результат выполнения каждый инструмента передается на вход следующему по порядку.

Синтаксис

Bash использует нестрогий синтаксис; вы можете использовать точку с запятой в конце строки, если хотите, и отступы не влияют на выполнение кода. В нестрогости синтаксиса кроется ловушка. Синтаксис bash важен и очень специфичен. Вдобавок, по сравнению с другими языками, ошибки в синтаксисе трудно диагностировать.

Очень важно правильно использовать пробелы и точки с запятой.

Например, можно получить ошибку “[grep isn’t a valid command”, если забыть поставить пробел внутри квадратных скобок [] или “Unexpected end of file”, когда вы забыли точку с запятой после {}.

При определении переменной пробел между переменной и знаком = и между знаком = и значением приводит к разным результатам. Существует важное различие между одинарными кавычками и двойными.

Синтаксические ошибки всегда выглядят как логические ошибки, что затрудняет выявление опечаток.

Структура

Сценарии оболочки понимают операторы управления выполнением: операторы if, циклы while, циклы for, операторы case и так далее.

Отличие bash от других языков — это условия и области видимости. Однако, поскольку bash в большей степени ориентирован на однострочные и одноразовые сценарии, условия используются не так часто, как в других языках.

Вот пример однострочника, который использует управление выполнением без каких-либо операторов if:

tac ~/error.log \
| grep -m1 -E "Error|Running restart" \
| grep -q "Error" \
&& echo "Found error since last restart"

Примечание: \ обозначают перенос строки, tac похож на cat, но выводит файл в обратном порядке.

Это выглядит уродливо, но эффективно, и иллюстрирует сильные и слабые стороны сценариев оболочки.

Сценарий bash может быть очень кратким и трудным для чтения. Вы можете многое сделать в несколько строк, но когда что-то сломается, может быть трудно понять, почему. Это благословение и проклятие. С большой силой появляется огромный потенциал, чтобы все испортить.

Что такое поток? Что такое команда?

Каждая команда — это программа, которая делает одну вещь. Grep, например, ищет вещи и возвращает строки. Запросы и файлы подаются на вход, найденные строки идут с выхода.

Вы можете подумать: «Да ладно, именно так работает все программирование», но в данном случае все немного сложнее, и это особенно важно понять.

Входы и выходы передаются от команды к команде в виде текстовых потоков. Есть три места, куда эти потоки идут и откуда берутся:

  • stdin: Стандартный ввод.
  • stdout: Стандартный вывод.
  • stderr: Стандартный вывод ошибок.

Это называется «поток», потому что строки выводятся в разных точках выполнения команды/функции, а не в конце, как вы могли бы подумать.

Вы отправляете текст на стандартный вывод с помощью таких команд, как printf и echo. Неопытный программист может думать, что это просто команды для вывода сообщений отладки, как в Python или JavaScript. Это не так.

Потоки позволяют объединять команды и функции. Хороший способ проиллюстрировать это — объяснить, как работают функции.

Функции

Определим функцию:

function hello () {
    printf "Hello World! \n"
    local variable="Something \n"
    printf "$variable"
    echo some more stuff to print $variable
}

Если запустить в терминале $ hello.sh, вы получите:

Hello World!
Something
some more stuff to print Something

Команды echo и printf отправляют текстовые потоки на стандартный вывод. Если вы запустите нашу функцию приветствия из терминала, stdout будет выведен на вашу консоль.

Мы можем перенаправить вывод и отправить его в файл или в качестве ввода для другой команды.

Это немного похоже на возвращаемые значения в обычных функциях языков программирования, за исключением того, что вы можете возвращать столько значений, сколько хотите, и они не завершают функцию.

Если вы хотите завершить функцию, для этого есть пара команд: return и exit. Команды выхода и возврата принимают числовой код: 0 означает успех, все остальное означает сбой. Команда return завершит работу функции, а команда exit завершит работу самой оболочки.

Коды выхода представляют собой целое число от 0 до 255 без знака. Если по какой-то причине вашему сценарию нужно более 255 различных способов потерпеть неудачу, то вам не повезло.

Перенаправление потоков

Вот основные моменты:

  1. | называется каналом, и вы используете его для отправки вывода другим командам. Например, мы можем попробовать hello | grep ‘Hello’. Эта конструкция отправит весь вывод команды hello в grep, который вернет строки, содержащие «Hello». Мое любимое повседневное использование каналов — history | grep «команда», когда я забыла точную команду, которую я набрала ранее, но я знаю, что в ней есть определенное слово.

  2. > с именем файла справа перенаправит вывод и запишет его в файл, а не на консоль. Файл будет полностью перезаписан. Например, logging_function> tmp_error.log. Если вам нравится Python, вы можете попробовать pip freeze> needs.txt.

  3. >> похоже на >, но дописывает в файл, а не перезаписывает его. Например, logging_function >> error.log.

  4. < обратно к > . Это перенаправление отправляет содержимое файла справа команде слева. Попробуйте grep foo <foo.txt. < is the reverse of. >

Каналы работают параллельно. Например, следующее будет работать только в течение одной секунды:

sleep 1 | sleep 1 | sleep 1 | sleep 1 | sleep 1

Участники канала не заставляют следующую команду в очереди ждать, пока они не будут полностью выполнены. Они обрабатывают и отправляют вывод по мере поступления.

Условия if

Ничто не иллюстрирует «маленькие инструменты» лучше, чем условие if в bash, которое на самом деле состоит из пяти ключевых слов:

if [ <expression> ]; then
<commands>
fi

Заметили, что условие заканчивается ключевым словом fi?

Так же и с оператором case … esac. Когда я узнала об этом, я понадеялась, что while будет завершено с помощью elihw, а until — с litnu. Но это, к сожалению, не так — эти операторы завершаются ключевым словом done.

[ это команда, а ] — это аргумент, который говорит прекратить принимать другие аргументы. If, else, elif и fi являются ключевыми словами.

Вот, например, ошибка:

/bin/sh: 1: [: true: unexpected operator

Вы можете подумать, что скрипт столкнулся с ошибочным [ и выдал синтаксическую ошибку. Это не так!

На самом деле происходит то, что команда [ получила неожиданный аргумент: true. Ошибка была на самом деле, потому что я использовала == вместо =, что было вычислено как true и неправильно при использовании оператора [.

Контроль выполнения

Я предпочитаю использовать if только там, где это действительно необходимо. Обычно я предпочитаю операторы bash: && и ||.

&& или || ставится после команды/функции. Если команда возвращает код 0, то команда справа от && будет выполнена, а команда справа от || нет:

$ will_return_0 && echo "I will print"
$ will_return_0 || echo "I will not print"
$ will_return_1 || echo "I will print"
$ will_return_1 && echo "I will not print"

Команды можно объединять в цепочки. Запустите следующие команды:

$ /bin/true && echo "I will print" || echo "I will not print"
$ /bin/false && echo "won't print" || echo "will print"

Но будьте внимательны! Порядок применения важен, нужно использовать вначале &&, а потом ||, но не наоборот. Если выполнить приведенную ниже команду:

/bin/false || echo "will print" && echo "won't print"

То в результате будет выведено обе строки, а не одна, как ожидалось.

Эти конструкции чуть сложнее читать, если вы не привыкли к языку командных сценариев. Условия if удобнее, если вам нужно сгруппировать несколько команд и нет смысла выносить эти команды в отдельную функцию.

Я также использую команду: test. Это то же самое, что и [ без вводящего в заблуждение синтаксиса. Менее знакомый синтаксис, мне кажется, лучше, потому что он сигнализирует читателю, что тот может не понимать, что именно происходит.

Переменные

Переменные в bash «дикие». Они работают так, как будто вы поместили их значение в скрипт и запустили его.

Переменные не имеют типа (число, строка, массив и так далее) и действуют, как нужно в данный момент: как строка, команда, число, несколько чисел и так далее. Они могут даже интерпретироваться как несколько ключевых слов, если в вашей «строке» есть пробелы.

Такое поведение переменных может привести к некоторым ошибкам, поэтому вам никогда не следует принимать рискованный ввод данных от пользователя в сценарий оболочки (например из интернета). Если вы веб-разработчик и знаете, насколько опасен eval, то каждый сценарий оболочки — это гигантский оператор eval. Удаленное выполнение кода в любом количестве!

Для примера введите в терминале:

$ MY_VAR="echo stuff"
$ $MY_VAR

Вы должны увидеть выполнение команды, и вывод «stuff» на консоль. Такое поведение может сделать длинные скрипты глючными и непредсказуемыми. Например, попробуйте такой код:

$ HELLO="hello world"
$ test $HELLO = "hello world" && echo yes

Выполнение вызовет ошибку, потому что bash читает код как test hello world = “hello world”.

Именно поэтому одной из лучших практик считается всегда помещать переменные в двойные кавычки:

test "$HELLO" = "hello world" 
 [ "$HELLO" = "hello world" ]

Двойные кавычки в bash это не ограничители строк. Bash не обрабатывает строки так, как другие языки. Кавычки немного больше похожи на круглые скобки в других языках (но не на скобки bash — это подоболочки).

Одиночные и двойные кавычки различаются в bash. В большинстве случаев используются двойные кавычки. В чем разница? Двойные кавычки расширяют переменные, одинарные кавычки понимают их буквально. Например:

var=stuff
echo $var
echo "$var"
echo '$var'

Другой раздражающей или полезной (в зависимости от точки зрения) особенностью переменных в bash является то, что в bash не предусмотрена ошибка о том, что переменная не объявлена. Вы можете проверить, была ли переменная установлена следующим образом:

test -z "$empty" && echo "variable is empty"

Также можно добавить параметр в сценарий, который покажет неустановленные переменные:

Переменные доступны для всей оболочки по умолчанию, но это можно изменить.

Область видимости

Понимание области видимости крайне важно, чтобы избежать ошибок.

Возможности которыми недостаточно пользуются, — это использование переменных local и readonly. local ограничивает переменную функцией, в которой она определена, а readonly вызовет ошибку, если вы попытаетесь переопределить переменную. Вы даже можете использовать эти опции вместе и сделать локальную переменную только для чтения.

Имя глобальной переменной должно быть ЗАГЛАВНЫМИ буквами. Чтобы сделать переменную доступной для всего терминала, используйте export VAR=”value”.

Верхний регистр говорит о том, что это глобальная переменная, а не о том, что это константа/неизменяемая переменная, как в других языках.

Команды

Моя самая нелюбимая вещь в bash — это запоминание маленьких загадочных команд; sed? cat? Что это значит? Имя, конечно, не скажет мне. Man зачастую трудны для понимания, и я, конечно, не вспомню, в каком порядке все должно идти.

Эта ситуация пришла из старых времен, когда каждый символ значил больше, а идея о том, что код должен быть удобочитаемым, не была так распространена.

Иногда поиск точной информации о том, что именно должна делать команда, напоминает поиск в пыльных архивах огромной библиотеки. К счастью, попадаются иногда супер-волшебники в Stack Overflow, которые не против поделиться своими с трудом заработанными знаниями.

Специальные переменные

Иногда вы сталкиваетесь со странными бессмысленными переменными, такими как $@ и $!. Вот их полный список.

Полезные переменные для написания скриптов и однострочников: $– и $*.

Они обе дают вам аргументы, с которыми работает команда/функция: $- — дает флаги, а $1-9 — ввод. Например:

Флаг — это -s, а example.com — это ввод.

Получая входные ключевые слова с $, важно использовать его так: ${2}, ${25} и так далее. Bash не поймет что-то вроде $42, если есть две цифры. Можно также сделать что-то вроде этого:

iterator=4
echo ${$iterator}

Подпроцессы и скобки

Если вы видите такую конструкцию:

То эти параметры вовсе не то, что вы думаете.

Скобки в bash на самом деле порождают вложенные процессы. Это означает, что они являются подпроцессами одного и того же сценария. По сути, это дочерние оболочки, которые получают весь родительский контекст (переменные, функции и так далее), но не могут ничего изменить в родительском процессе.

Подпроцессы — это вторая важная вещь после переменных, которую нужно понимать в сценариях оболочки, чтобы сохранить здравый ум. Они могут появляться там, где вы их не ожидаете, и делать ваши программы менее предсказуемыми.

Во-первых, что они делают?

Давайте рассмотрим примеры:

!#/bin/bash
myVar="foo"
echo $myVar
(
    echo "Inside the sub-shell"
    echo $myVar
    myVar="bar"
    echo $myVar
)
echo "Back in the main shell now"
echo $myVar
foo
Inside the sub-shell
foo
bar
Back in the main shell now
foo

Обратите внимание, как переменная была изменена для контекста подпроцесса, но не за его пределами. Вот еще одна вещь, которая удобна для подпроцессов:

!#/bin/bash
echo "We start out in home"
pwd
(
   echo "In the subshell"  
   cd /tmp
   pwd
)
echo "Back in the main shell now"
pwd
We start out at root
/home/username
In the subshell
/tmp
Back in the main shell now
/home/username

Использование exit в подпроцессе приведет к выходу только из этого подпроцесса, а не из родительского сценария.

Круглые скобки — не единственный способ создания подпроцесса. Если вы поместите процесс в фоновый режим с помощью & или nohup, он также перейдет в подпроцесс.

Скрипты, которые вы запускаете с помощью ./, запускаются в своем собственном процессе, а не в подпроцессе вашего терминала, тогда как скрипты, которые вы запускаете с помощью команды source, запускаются так, как если бы вы вводили команды напрямую.

Вы можете ожидать, что функции будут выполняться в подпроцессах, но на самом деле это просто группировка команд.

Чтобы ваша функция запускалась в подпроцессе каждый раз, когда вы ее используете, вам нужно обернуть ее в скобки. Чтобы ваша функция возвращала определенный код завершения без запуска в подпроцессе и не вызывала выход из всего сценария, используйте return вместо exit.

Разделение на части

Иногда вам может потребоваться разбить ваш скрипт на файлы. Команда source поможет вам:

parent.sh:
!#/bin/bash
source /path/to/script.sh

В целом, это работает так, как будто вы добавили весь контент скрипта script.sh в скрипт parent.sh. Это значит, что если вы установите переменную в родительском скрипте, все скрипты, подключенные как source, будут иметь доступ к этой переменной.

Намного лучше использовать этот метод, как подключаемую библиотеку: импортировать вспомогательные функции, которые можно использовать в родительском скрипте.

Остерегайтесь кодов выхода и передачи значений в основной сценарий! Это не работает так, как вы думаете. Если в подключенном скрипте выполнение производится в основном процессе, то команда exit в подключенном скрипте завершит работу основного скрипта!

Рассказываем об IT-бизнесе, технологиях и цифровой трансформации

Подпишитесь в соцсетях или по email

Обработка ошибок

Я стараюсь группировать команды в функцию и обрабатывать ошибки, если функция не выполнилась:

my_function || handle_error

Понравилась статья? Поделить с друзьями:
  • Управление персоналом или руководство кадрами
  • Каметон аэрозоль инструкция по применению цена детям
  • Руководством по ремонту киа спектра скачать
  • Руководство магазина магнит телефон
  • Kia spectra скачать руководство по эксплуатации