Введение
Сценарий оболочки хранит команды в файле, чтобы задачу можно было выполнять повторно и одинаково. Надёжные сценарии для начинающих явно указывают интерпретатор, заключают раскрываемые переменные в кавычки, проверяют входные данные перед выполнением действий и возвращают код завершения, понятный другим командам.
В этой лабораторной работе вы будете постепенно создавать небольшие сценарии Bash. Вы запустите сценарии через Bash и как исполняемые файлы, используете переменные и позиционные аргументы, считаете интерактивный ввод, организуете ветвление с помощью if и проверок файлов, выберете коды завершения, выполните итерации с помощью for и объедините эти приёмы в простом суммаризаторе журналов.
Создайте и запустите первый сценарий
На этом шаге вы создадите сценарий Bash, запустите его через интерпретатор и сделаете его непосредственно исполняемым.
Перейдите в рабочий каталог лабораторной работы:
cd /home/labex/project/shell-lab
Создайте hello.sh с помощью here-документа. Первая строка — это шебанг: он выбирает Bash, когда файл запускается напрямую:
cat > hello.sh <<'EOF'
#!/bin/bash
echo "Hello from a Bash script"
pwd
EOF
Проверьте содержимое файла перед запуском:
cat hello.sh
Явно передайте файл Bash. Это работает даже до добавления разрешения на выполнение:
bash hello.sh
Добавьте разрешение на выполнение и проверьте режим доступа:
chmod u+x hello.sh
ls -l hello.sh
Запустите файл из текущего каталога. ./ задаёт путь к файлу; обычно оболочка не ищет команды в текущем каталоге автоматически:
./hello.sh
Используйте переменные и безопасные кавычки
На этом шаге вы присвоите значения переменным, раскроете их и увидите, почему двойные кавычки защищают значения, содержащие пробелы.
Создайте сценарий с двумя переменными:
cat > variables.sh <<'EOF'
#!/bin/bash
course="Linux for Noobs"
workspace="/home/labex/project/shell-lab"
echo "Course: $course"
echo "Workspace: $workspace"
printf 'Quoted argument count: %s\n' "$#"
printf 'First argument: %s\n' "${1:-not provided}"
EOF
В присваивании оболочке вокруг = не ставят пробелы. Двойные кавычки позволяют раскрыть $course, сохранив Linux for Noobs как одно значение.
Запустите сценарий с одним аргументом, содержащим пробелы:
bash variables.sh "beginner learner"
Специальная переменная $# содержит количество позиционных аргументов, а $1 — первый аргумент. ${1:-not provided} подставляет резервное значение, если первый аргумент отсутствует.
Сохраните вывод для проверки:
bash variables.sh "beginner learner" > variable-output.txt
cat variable-output.txt
Проверяйте позиционные аргументы
На этом шаге вы потребуете обязательный аргумент, выведете подсказку по использованию и будете возвращать разные коды завершения при ошибке и успехе.
Создайте greet-argument.sh:
cat > greet-argument.sh <<'EOF'
#!/bin/bash
if [ "$#" -ne 1 ]; then
echo "Usage: $0 NAME" >&2
exit 2
fi
name=$1
echo "Hello, $name"
exit 0
EOF
Тестовая команда [ ... ] проверяет, не отличается ли количество аргументов от единицы. >&2 отправляет текст с подсказкой в стандартный поток ошибок, а exit 2 сообщает о некорректном использовании сценария.
Запустите сценарий без аргумента, а затем сразу выведите $? — код завершения предыдущей команды:
bash greet-argument.sh
echo "missing argument status: $?"
Ожидаемый код — 2. Теперь запустите сценарий правильно:
bash greet-argument.sh "Ada Lovelace"
echo "valid argument status: $?"
Сохраните приветствие при успешном выполнении:
bash greet-argument.sh "Ada Lovelace" > argument-greeting.txt
Считывайте интерактивный ввод
На этом шаге вы выведете приглашение пользователю, безопасно считаете одну строку и обработаете пустой ввод.
Создайте ask-name.sh:
cat > ask-name.sh <<'EOF'
#!/bin/bash
read -r -p "Enter your name: " name
if [ -z "$name" ]; then
echo "A name is required" >&2
exit 1
fi
echo "Welcome, $name"
EOF
read -r считывает строку, не обрабатывая обратные косые черты особым образом. -p выводит приглашение, а -z проверяет, является ли значение в кавычках пустым.
Запустите сценарий:
bash ask-name.sh
В ответ на приглашение введите:
Grace Hopper
Вы должны увидеть Welcome, Grace Hopper. Чтобы получить воспроизводимый сохранённый результат, передайте одну строку ввода в тот же сценарий через конвейер:
printf 'Grace Hopper\n' | bash ask-name.sh > interactive-greeting.txt
cat interactive-greeting.txt
Организуйте ветвление с помощью проверок файлов
На этом шаге вы используете if, elif и else вместе с проверками файлов, чтобы определить тип пути.
Создайте classify-path.sh:
cat > classify-path.sh <<'EOF'
#!/bin/bash
path=${1:-}
if [ -z "$path" ]; then
echo "Usage: $0 PATH" >&2
exit 2
elif [ -d "$path" ]; then
echo "directory: $path"
elif [ -f "$path" ]; then
echo "file: $path"
else
echo "missing: $path" >&2
exit 1
fi
exit 0
EOF
-d проверяет, является ли путь каталогом, а -f — обычным файлом. Порядок проверок важен: сначала обрабатывается некорректное использование, а затем определяется тип пути.
Проверьте все три возможных результата:
bash classify-path.sh logs
bash classify-path.sh notes.txt
bash classify-path.sh missing.txt
echo "missing path status: $?"
Сохраните успешные результаты вместе. && запускает вторую команду только в том случае, если первая завершилась успешно:
bash classify-path.sh logs > path-results.txt && bash classify-path.sh notes.txt >> path-results.txt
cat path-results.txt
Обработайте несколько файлов с помощью цикла for
На этом шаге вы переберёте файлы журналов и подсчитаете совпадающие записи в каждом из них.
Создайте count-errors.sh:
cat > count-errors.sh <<'EOF'
#!/bin/bash
log_dir=${1:-logs}
if [ ! -d "$log_dir" ]; then
echo "Log directory not found: $log_dir" >&2
exit 1
fi
for log_file in "$log_dir"/*.log; do
error_count=$(grep -c '^ERROR ' "$log_file" || true)
printf '%s errors=%s\n' "$(basename "$log_file")" "$error_count"
done
EOF
На каждой итерации цикла переменная log_file получает один найденный путь. Кавычки вокруг "$log_file" защищают пробелы в пути, а basename удаляет префикс каталога. grep -c подсчитывает совпадающие строки, а || true не позволяет отсутствию совпадений остановить более крупный рабочий процесс со строгой обработкой ошибок.
Запустите цикл и сохраните его вывод:
bash count-errors.sh logs | tee error-counts.txt
Вы должны увидеть один результат для каждого файла .log.
Объедините проверки, циклы и коды завершения
На этом шаге вы создадите один повторно используемый сценарий, который проверяет каталог, суммирует его журналы и явно сообщает об успехе или ошибке.
Создайте summarize-logs.sh:
cat > summarize-logs.sh <<'EOF'
#!/bin/bash
input_dir=${1:-}
output_file=${2:-}
if [ -z "$input_dir" ] || [ -z "$output_file" ]; then
echo "Usage: $0 INPUT_DIR OUTPUT_FILE" >&2
exit 2
fi
if [ ! -d "$input_dir" ]; then
echo "Input directory not found: $input_dir" >&2
exit 1
fi
: > "$output_file"
for log_file in "$input_dir"/*.log; do
lines=$(wc -l < "$log_file")
errors=$(grep -c '^ERROR ' "$log_file" || true)
printf '%s lines=%s errors=%s\n' "$(basename "$log_file")" "$lines" "$errors" >> "$output_file"
done
echo "Wrote summary to $output_file"
exit 0
EOF
Команда : > "$output_file" безопасно создаёт файл назначения или очищает его перед началом цикла. Запустите сценарий с обоими обязательными аргументами:
chmod u+x summarize-logs.sh
./summarize-logs.sh logs log-summary.txt
echo "summary status: $?"
cat log-summary.txt
Проверьте ветку обработки ошибки с отсутствующим каталогом:
./summarize-logs.sh missing log-summary.txt
echo "missing directory status: $?"
При успешном выполнении код должен быть 0, а при отсутствии каталога — 1.
Итоги
Вы создали и запустили сценарии Bash, использовали шебанги и разрешения, раскрывали переменные в кавычках, считывали позиционные и интерактивные данные и возвращали содержательные коды завершения. Кроме того, вы организовывали ветвление с помощью if/elif/else, проверяли файлы и каталоги, перебирали журналы циклом for и объединили эти приёмы в повторно используемом суммаризаторе.
Эти основы помогают писать автоматизацию, которую проще читать и безопаснее запускать повторно, а другим командам и заданиям планировщика — проще оценивать.



