Введение
Скрипт оболочки хранит команды в файле, чтобы задачу можно было выполнять повторно и предсказуемо. Надёжные скрипты для начинающих явно указывают интерпретатор, заключают подстановки переменных в кавычки, проверяют входные данные перед выполнением действий и возвращают код завершения, понятный другим командам.
В этой лабораторной работе вы будете постепенно создавать небольшие 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 и объединили эти приёмы в универсальном анализаторе.
Эти базовые навыки помогают создавать автоматизацию, которую проще читать, безопаснее запускать повторно и удобнее проверять другим командам или планировщикам заданий.



