Статья Введение в Bash для хакеров. Часть 2

Squ0nk

Green Team
30.10.2017
211
83
BIT
146
[Массивы]
В сценариях вы можете захотеть сохранить набор элементов вместо переменной. Для этого можно использовать массивы. В bash, чтобы определить массивы, мы помещаем значения в круглые скобки с пробелами. Ниже у нас есть пустой массив под названием a, а затем мы определили другой массив под названием b. Для извлечения значения из массива мы используем индекс, основанный на нуле. Например, ниже, поставив индекс 2, мы извлекаем третье значение из массива b:
Bash:
#!/bin/bash
a=()
b=("reason" "logic" "aspect" "ethic")
echo ${b[2]}
Теперь, если мы запустим скрипт, то, как вы догадываетесь, будет выведено слово «aspect»:
1741148208998.webp

Мы также можем задать индекс массива числом:
b[5] = "ground"
Вы также можете использовать оператор +=, чтобы добавить значение в конец массива:
b+=("rationale")
Вы можете использовать индекс @ для просмотра всего массива:
Bash:
#!/bin/bash
a=()
b=("reason" "logic" "aspect" "ethic")
b[5]="rationale"
b+=("rationale")
echo ${b[2]}
echo ${b[@]}
Результат будет выглядеть следующим образом:
1741148358669.webp

[Файлы]
Одна из главных потребностей в bash - работа с текстовыми файлами. Основным ключом в этой области являются символы < и >. Например, следующая команда сохраняет указанный текст в файл с именем text.txt.
echo "something" > text.txt
С помощью приведенной выше команды, если файл существует, его содержимое будет заменено, а если файла нет, в него будет вставлено указанное содержимое. Если нужно очистить содержимое файла, можно использовать следующую структуру команды:
> text.txt
Если вы хотите добавить значение в конец файла и сохранить предыдущее значение, используйте символ >>
echo "stuff" >> text.txt
До сих пор мы говорили о вставке содержимого в файл. Как прочитать содержимое файла в сценарии?
Для этого можно использовать цикл while с командой read. Приведенная ниже синтаксическая структура заставляет читать содержимое файла text.txt построчно и сохранять его в переменной text. Затем мы выводим содержимое внутри цикла с помощью команды echo.
Bash:
#!/bin/bash
while read text;
do
 echo $text
done < text.txt
1741148528280.webp

Точно так же, как вы можете использовать файл в качестве входа в цикл, вы можете использовать файл в качестве входа в команду. Простой пример - использование команды cat для чтения файла:
cat < text.txt
Более удачным примером является сохранение команд, необходимых для выполнения, в текстовом файле, а затем ввод содержимого файла в командную строку для использования. Например, в сценарии необходимо подключиться к определенному FTP-серверу, загрузить файл, а затем выйти из него. Для этого мы сначала создадим текстовый файл и сохраним в нем нужные команды.
Ниже мы сначала откроем mirror.xmission.com с открытым FTP-сервером, подключимся с анонимным пользователем и паролем none, загрузим файл и выйдем:
1741148637059.webp

1741148650154.webp

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

1741148674016.webp

[Структуры условий]
Оператор if выполняет наш код в зависимости от заданных условий. Структура оператора if такова, что сначала вставляется фраза if, а затем логическое выражение:
Код:
if [ expr ]
if [[ expr ]]
if (( expr ))
if expr
Можно использовать все четыре вышеперечисленных режима. Две круглые скобки используются для сравнения целых чисел, как мы уже говорили. Использование каждой из них зависит от ваших потребностей. Структура if выглядит следующим образом:

if (( 10>5 ))
then
echo "true"
else
echo "false"
fi
Сначала проверяется условие в выражении. Если условие выполняется, то после оператора then будет выполнен блок кода. Затем, если выражение не выполнено, будет выполнен блок else. Наконец, мы закрываем оператор if с помощью fi:

1741148784076.webp

Если вам нужно больше значений в операторе if, вы можете использовать elif:
Код:
n1=10
n2=5
if (( $n1>$n2 ))
    then
    echo "n1 is greater than n2"
elif (( $n1<$n2 ))
    then
    echo "n1 is lower than n2"
else
    echo "n1
Сначала мы присваиваем переменной n1 числовое значение 10, а переменной n2 - 5. Затем с помощью целых чисел мы проверяем, больше ли переменная a 5 или нет. Если больше, то будет выведено сообщение.
Результат будет выглядеть так, как показано ниже:
1741150075665.webp

Если мы изменим переменную n1 на 4, будет выполнен блок elif:
1741150090074.webp

Если мы изменим переменную n1 на 5, будет выполнен блок else:
1741150108917.webp

[While]
Цикл используется в программировании для повторения операции до определенного числа. Например, если мы хотим вызвать функцию сто раз, то нет смысла писать сто строк кода для такой простоты. В bash существует несколько способов создания цикла, один из которых - while. Пример простого цикла while показан ниже. Общая структура работы выглядит следующим образом:
Bash:
#!/bin/bash
i=0
While [ $i -le 10 ]
do
 echo i:$i
 ((i+=1))
done
Здесь мы сначала создаем переменную-счетчик с именем i и начальным значением ноль. В следующих строках мы определяем условие while таким образом, что если значение переменной i меньше или равно 10, то цикл будет продолжен. А если i больше 10, то цикл завершается. Наконец, мы выводим значение i внутри цикла, а затем прибавляем к нему число:

1741150194206.webp

[For]
Еще одним типом цикла в bash является тип for, который создает цикл на основе набора показателей, обычно одного или определенного диапазона. Используя этот тип цикла, мы получаем переменную, которая находится в определенном диапазоне. Простой пример можно увидеть ниже:
Bash:
#!/bin/bash
i=0
for i in 1 2 3 4 5
do
    echo i:$i
done
В начале цикла мы определяем переменную i и ограничиваем диапазон чисел от 1 до 5.
Запуск этого простого скрипта привел к печати чисел от одного до пяти:
1741150275697.webp

Записывать числа по одному нерационально, если мы хотим использовать больший диапазон чисел. Поэтому, как мы уже объясняли ранее, для определения диапазона чисел или строк можно использовать фигурные скобки {}:
Код:
i=0
for i in {1..5}
do
    echo i:$i
done
Результаты будут следующими:
1741150344541.webp

Другой тип структуры цикла for, который знаком вам, если вы работали с языком C, выглядит следующим образом:

Bash:
#!/bin/bash

for (( i=1; i<=10; i++ ))
do
 echo $i
done
Здесь мы сначала определяем начальное значение переменной i, а затем ставим условие цикла. Наконец, добавляем число к счетчику i (счетчик также может быть: i+=2). Все эти элементы должны быть заключены в круглые скобки. Между каждым помещаемым значением должна стоять точка с запятой ;:

1741150405103.webp

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

Bash:
#!/bin/bash

for i in $(ls)
do
 echo $i
done
Для этого мы добавляем команду ls в диапазон цикла замены.
1741151938953.webp

Таким образом, результаты выполнения команды ls выводятся построчно.
[Case]
Что делать, если вам нужно проверить множество различных элементов? Можно использовать длинный список операторов if, но есть лучший способ. Для этой цели мы используем термин case. Оператор case проверяет значение на основе списка заданных значений. Рассмотрим следующий пример:
Bash:
#!/bin/bash
$web="example.com"
case $web in
 forest.com) echo "It's forest.";;
 animal|human) echo "Maybe animal or maybe human";;
 example.com) echo "This is a site!";;

*) echo $web;;
esac
Сначала мы определяем переменную site и присваиваем ей значение. Затем мы используем переменную $web в качестве основы и помещаем элементы проверки в отдельную строку. В каждом случае мы сначала вводим проверяемое значение, а затем добавляем закрытую круглую скобку ) и добавляем нужный код в случае успешной проверки. С помощью символа | мы можем проверить несколько вещей вместе. С помощью знака * мы можем проверить другие элементы, не упомянутые в списке. Обратите внимание, что в конце каждой строки необходимо поставить две точки с запятой. Наконец, мы закрываем регистр с помощью esac (напротив слова case). При выполнении, после проверки всех режимов до того, как будет проверен правильный режим, и в конце, блок будет выполнен соответствующим образом:
1741152049907.webp

Теперь изменим веб-переменную на «terrestrial»:
1741152076286.webp

[Функции]
В процессе разработки скрипта вы можете заметить, что многие фрагменты кода повторяются. Вы можете использовать функции, чтобы предотвратить дублирование блоков кода и лучше организовать сценарий. Создавать функции в bash очень просто. Просто введите ключевое слово function с желаемым именем функции и введите код функции в круглых скобках. Вот простой пример функции в сценарии bash. После определения функции, чтобы вызвать ее, достаточно ввести имя функции за ее пределами.
Bash:
#!/bin/bash

function greet {
 echo "Hi there!"
}

greet
Выше мы создали функцию greet и вызвали ее из функции:

1741152153629.webp

Вы можете указывать аргументы для функций. Например, ниже мы добавляем аргумент $1 в нашу функцию, а вне функции добавляем аргумент name в функцию, вызывая ее:

Bash:
#!/bin/bash

function greet {
 echo "Hi $1"
}

greet Anna
1741239555400.webp

Здесь мы добавляем еще три аргумента в нашу функцию.

Код:
function greet {
 echo "Hi $1, $2, and $3"
}

greet Anna Sara Alec
Эти аргументы вводятся в порядке $1, $2, $3,... call и помещаются в функцию.

1741239586770.webp

[Аргументы]
До сих пор мы писали скрипты, которые не принимали никаких вводных данных. В реальном мире вам часто приходится получать данные от пользователя. Для этого мы используем аргументы. Если вы читали раздел о функциях, то увидите, что эти аргументы точно такие же, как и аргументы функций. Аргументы - это специальные переменные, которые задаются пользователем во время выполнения скрипта. Они могут быть практически любыми. Файл или папка, которую вы хотите создать, имя пользователя или текстовая строка для поиска.
Эти аргументы вставляются в момент выполнения после имени пользовательского скрипта. Аргументы представляются в виде пронумерованных переменных и назначаются в порядке ввода в командную строку. Как и раньше, $1, $2, $3, ... вы видите простой пример ниже:
Bash:
#!/bin/bash
echo $1
echo $2
echo $3
Выше мы определили три аргумента для скрипта и вывели их значение. При задании аргументов пользователем, если они содержат пробелы, их следует заключать в кавычки:

1741239663619.webp

Если мы хотим не ограничивать количество аргументов в переменной, вместо того чтобы вручную присваивать каждый аргумент переменной, мы можем создать массив аргументов с помощью $@ и цикла for. Вы также можете подсчитать количество присвоенных аргументов с помощью переменной $#:

Bash:
#!/bin/bash
for i in $@
do
 echo $i
done
echo "The number of arguments: $#"
В этом случае пользователь может ввести в скрипт любое количество аргументов:
1741239716769.webp

[Флаги]
Если вы работали с инструментом командной строки, вы знакомы с флагами. Флаги - это специальные опции, которые вы можете использовать для передачи информации программе. Флаги обычно начинаются с символа Dash. Вы можете использовать функцию getopts для использования флагов в своем скрипте. Предположим, мы хотим создать скрипт, принимающий имена пользователей и пароли. Вы можете увидеть следующий пример.

Bash:
#!/bin/bash

while getopts u:p: option;
do
 case $option in
 u) user=$OPTARG;;
 p) pass=$OPTARG;;
 esac
done
echo "Username: $user"
echo "Password: $pass"
Здесь мы используем функцию getopts для создания цикла while со строками опций внутри кейса. Сначала мы вводим u:p:, что определяет, что нашими опциями будут -u и -p. Затем внутри оператора case мы помещаем переменную option в качестве основы для оценки и определяем варианты.
$OPTARG - это значение, которое посылает скрипту пользователь в командной строке. Вы увидите, что заданные нами значения добавляются:

Здесь мы используем функцию getopts для создания цикла while со строками опций внутри кейса. Сначала мы вводим u:p:, что определяет, что нашими опциями будут -u и -p. Затем внутри оператора case мы помещаем переменную option в качестве основы для оценки и определяем варианты.

$OPTARG - это значение, которое посылает скрипту пользователь в командной строке. Вы увидите, что заданные нами значения добавляются:
1741239762941.webp

Флаги хороши тем, что нам не нужно соблюдать порядок при их импорте.
[read]
Мы рассмотрели, как вводить пользовательские данные в скрипт через командную строку. Но во многих случаях установка всех входных значений в скрипте нецелесообразна. Вы можете получить ввод от пользователя во время выполнения скрипта. Если вы знакомы с Python, использование read похоже на функцию ввода в Python.

Для этого используйте ключевое слово read. Слово read останавливает работу скрипта, получает ввод от пользователя и сохраняет его в указанной переменной для последующего использования. Например, ниже мы хотим получить от пользователя целевой IP-адрес и сохранить его в переменной ip:
Bash:
#!/bin/bash
echo "Target IP Address"
read ip
echo "Target IP Address is: $ip"
Здесь вы получите IP и просто напишите его:
1741239838498.webp

Есть и другие особенности. Рассмотрим следующий пример:
Bash:
#!/bin/bash
echo "Please enter a username"
read user
echo "Please enter a password"
read -s pass
read -p "The IP Address?" ip
echo username: $user password: $pass IP Address: $ip
При вводе опции -s пользовательский ввод не отображается. Эта опция обычно используется при вводе пароля, поскольку мы не хотим, чтобы он отображался на консоли. Вы можете записать все в одну строку, добавив опцию -p:
1741239880268.webp

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

[select]
Еще один способ получить ввод от пользователя - использовать ключевое слово select, которое работает как меню. Для этого сначала введите ключевое слово select, затем имя вашей переменной, затем слово in, затем список вариантов для выбора. Затем вводим слово do, а затем содержимое исполняемого файла. Здесь мы выводим только то значение, которое выбрал пользователь с помощью команды echo. Очень важно завершить цикл ключевым словом break:
Bash:
#!/bin/bash

select ogy in "Psychology" "Psychiatry" "Criminology" "Astrology"
do
    echo "Your selected major is $ogy"
    break
done
При выполнении приведенного выше сценария пользователю отображается список выбранных пунктов. Когда пользователь сможет ввести нужный номер, он сможет определить выбранный маршрут.

1741239939070.webp

Эта функция также может быть написана с использованием структуры case. Введите здесь оператор case и создайте для каждого варианта свой вывод. Обратите внимание, что мы добавили опцию Exit, которая, если пользователь введет *, выполнит break и выйдет из программы. Опция * также вызывает другие элементы, которых нет в списке:

Bash:
select ogy in "Psychology" "Psychiatry" "Criminology" "Astrology"
do
    case $option in
    "Psychology")  echo "Psychology is the science of mind and behavior.";;
    "Psychiatry")  echo "Psychiatry is the medical specialty devoted to the diagnosis, prevention, and treatment of mental disorders.";;
    "Criminology") echo "Criminology is the study of crime and deviant behaviour.";;
    "Astrology")   echo "Astrology is a pseudoscience that claims to divine information about human affairs and terrestrial events by studying the movements and relative positions of celestial objects";;
    "exit")    break;;
     *)     echo "Not defined"
 esac
done
Если мы запустим скрипт, то сначала введем опцию 2 для отображения вывода Psychiatry, а затем введем другую опцию. Наконец, мы выходим из программы:

1741239971522.webp

[Управление исключениями]
Хотя очень важно получить ввод от пользователя, вы также должны управлять возможными ошибками. Что произойдет, если пользователь не введет параметры и нажмет Enter? Правильно ли работает ваш скрипт в такой ситуации? Сначала приведем пример, когда для запуска скрипта требуется три аргумента:
Bash:
#!/bin/bash

if [ $# -lt 3 ];
then
    cat <<- END
    This script needs three arguments:
    ./exception <TARGET> <PORT> <SCHEME>
    END
else
 echo "$3://$1:$2"
fi
Этот скрипт сначала проверяет количество аргументов с помощью $# и, если количество введенных аргументов меньше трех, here-doc выводит текст в качестве подсказки для пользователя. Если количество аргументов правильное, то будут выведены введенные в скрипте данные:

1741240012773.webp

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

Другой способ - создать цикл, в результате которого пользователь должен только ввести значение, иначе он получит сообщение об ошибке:

Bash:
#!/bin/bash

read -p "hostname: " host
while [[ -z "$host" ]]
do
 read -p "hostname has not been entered!
hostname: " host
done
echo "Entered hostname is $host"
Это оповещение продолжается до тех пор, пока пользователь не введет требуемую сумму:

1741240042736.webp

Лучший способ - дать пользователю возможность выбрать ответ по умолчанию:

Bash:
#!/bin/bash
read -p "hostname: [default:localhost] " host
while [[ -z "$host" ]]
do
 host="localhost"
done
echo "entered hostname is $host"
Если пользователь нажмет кнопку ввода, то по умолчанию будет использоваться localhost. Опция -z означает, что переменная host не имеет значения:
1741240076166.webp

Еще один метод проверки данных - регулярные выражения:
Bash:
#!/bin/bash
read -p "hostname: " host
while [[ ! $host =~ [\w\.]+ ]]
do
 read -p "The hostname is incorrect:  $host
hostname: " host
done
echo "Entered hostname is $host"
Если вводимые данные не соответствуют заданным регулярным выражениям, мы получим предупреждение о необходимости ввести правильные данные:
1741240104567.webp
 
  • Нравится
Реакции: yetiraki и Edmon Dantes
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!