Скрипты


Скрипты

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

В Unix-среде принято называть такие файлы скриптами.


Напишем такой script.sh:

#!/bin/sh
## файл script.sh
 
echo "Hi there"

Скрипты

Чтобы скрипт можно было исполнять нужно сделать несколько вещей.

Во-первых, в начале скрипта нужно указать два специальных символа (тут правильнее сказать: два байта) she-bang: #!, а за ним путь к интерпретатору, с помощью которого нужно исполнить этот скрипт.

Без них операционная система не поймёт как выполнить код. А с ними запустит интерпретатор и передаст путь к файлу, который запускаем.

Интерпретатор может быть почти любым, не только shell.

В примере выше she-bang уже указан, скрипт будет интерпретироваться с помощью /bin/sh.


Скрипты

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

chmod u+x script.sh

Теперь скрипт можно исполнить отдельным процессом:

./script.sh
## вывод
Hi there

Скрипты

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

chmod u+x script.sh && ./script.sh
## вывод
Hi there

Аргументы в bash-скриптах

В скрипт можно передать аргументы, они будут доступны по именам $1, $2, $3 и т.д.; скрипт завершается кодом возврата:

#!/usr/bin/env sh
## script.sh
 
echo $1
echo $2
echo $3
 
exit 10 # если код не 0

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

chmod u+x script.sh
./script.sh "why braces?" '$1 $2 $3' three
echo $?

Полезные утилиты при работе с аргументами

Довольно полезной при работе с аргументами оказываеться утилита shift: shift n переименует переменные $N+1, $N+2, $N+3, … на $1, $2, $3, …


Полезные утилиты при работе с аргументами

#!/usr/bin/bash
 
i=1
 
while ! [ -z ${1+x} ]
do
    echo "Here is arg $i: $1"
 
    shift
    i=$((i+1))
done

здесь используем сложную подстановку переменной 1.

В общем виде подстановка ${var+str} работает так:

  • если переменная var не установлена, то подставляется пустая строка ''
  • если переменная var установлена, при этом неважно пустая или нет, то результатом подстановки будет значение str

Полезные утилиты при работе с аргументами

Так, условие ! [ -z ${1+x} ] будет возвращать код возарата 0 (всё ок), если в переменной 1 что-то содержится и только в этом случае, во всех остальных случаях условие будет возвращать код возрата 1

Если переменная 1 не установлена, т.е. нет первого аргумента у этого скрипта, то подстановка строки будет пустая строка '' и тогда [ -z ${1+x} ] вернёт код возврата 0, а ! [ -z ${1+x} ] соответственно 1.

Пользуемся этим и на каждой итерации работаем с первым аргументом, а в конце вызываем shift без аргументов, он переименует аргументы $2, $3, ... в $1, $2, ...

Таким образом скрипт обойдёт все переданные аргументы и выведет их по одному, добавив префикс Here is arg n: перед выводом каждого аргумента n. getopts, но их проще рассматривать вместе с конструкциями POSIX shell, поэтому вернёмся к их рассмотрению позже.

Подробнее о всяких сложных подстановках можно найти прямо в стандарте: Shell and Utilities: Detailed Toc#2.6.2 Parameter Expansion


Скрипты на других языках программирования

Обратите внимание как используется shebang в примерах ниже.

Скрипты можно писать и на awk и на sed и на других языках.

Но существуют ограничения: можно вызвать интерпретатор только с одним аргументом.
См. https://unix.stackexchange.com/questions/399690/multiple-arguments-in-shebang


Пример на Python

#!/usr/bin/env python3
import os
import sys
 
def list_files(directory):
    try:
        files = os.listdir(directory)
        for file in files:
            path = os.path.join(directory, file)
            if os.path.isfile(path):
                size = os.path.getsize(path)
                print(f"{file}: {size} bytes")
    except FileNotFoundError:
        print(f"Directory not found: {directory}")
 
if __name__ == "__main__":
    directory = sys.argv[1] if len(sys.argv) > 1 else '.'
    list_files(directory)

Пример на Python

Чтобы запустить

chmod +x list_files.py

а потом

./list_files.py /path/to/directory

Что скрипт делает, если в него не передали аргументы?


Пример на Perl

А вот пример на perl:

#!/usr/bin/perl
 
use strict;
use warnings;
 
sub count_lines {
    my ($filename) = @_;
    open(my $fh, '<', $filename) or die "Cannot open file $filename: $!";
    my $count = 0;
    $count++ while <$fh>;
    close $fh;
    return $count;
}
 
my $file = $ARGV[0] or die "Usage: $0 filename\n";
my $lines = count_lines($file);
print "$file has $lines lines\n";

Пример на Perl

Аналогично нужно дать права на исполнения

chmod +x count_lines.pl

и можно исполнить так

./count_lines.pl filename.txt