|
|
|
|
# Решения
|
|
|
|
|
|
|
|
|
|
## 0.
|
|
|
|
|
Например, diff <(ls foo) <(ls bar) покажет различия между файлами в каталогах foo и bar.
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ mkdir v1 v2
|
|
|
|
|
stud@stud12:~$ touch v1/a.txt v1/b.txt v2/b.txt v2/c.txt
|
|
|
|
|
stud@stud12:~$ diff <(ls v1) <(ls v2)
|
|
|
|
|
1d0
|
|
|
|
|
< a.txt
|
|
|
|
|
2a2
|
|
|
|
|
> c.txt
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 1.
|
|
|
|
|
Выполните следующие примеры:
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ false || echo "Opps, fail"
|
|
|
|
|
Opps, fail
|
|
|
|
|
stud@stud12:~$ true || echo "Opps, fail"
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ true && echo "Things went well"
|
|
|
|
|
Things went well
|
|
|
|
|
stud@stud12:~$ false && echo "Things went well"
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ true; echo "Always"
|
|
|
|
|
Always
|
|
|
|
|
stud@stud12:~$ false; echo "Always"
|
|
|
|
|
Always
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
*Что произойдёт в данном случае?
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ (echo red; echo green 1>&2) | echo blue
|
|
|
|
|
```
|
|
|
|
|
В большинстве случаев эта команда выводит "синий зеленый". Но примерно в 1% случаев она выводит "зеленый синий". А иногда выводится просто "синий". Для большей детерминированности добавим в пример задержки в 100мс. В реальности эта задержка на выполнение инструкций команды будет зависеть от загрузки системы и приоритетов выполнения задач.
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ (echo red; echo green 1>&2) | echo blue
|
|
|
|
|
blue
|
|
|
|
|
green
|
|
|
|
|
stud@stud12:~$ (echo red; echo green 1>&2) | (sleep 0.1; echo blue)
|
|
|
|
|
green
|
|
|
|
|
blue
|
|
|
|
|
stud@stud12:~$ (sleep 0.1; echo red; echo green 1>&2) | echo blue
|
|
|
|
|
blue
|
|
|
|
|
```
|
|
|
|
|
Мы имеем три порядка выполнения:
|
|
|
|
|
- `echo` красного цвета происходит до выхода `echo` синего цвета, поэтому левая сторона не получает сигнал SIGPIPE, но после этого происходит `echo` зеленого цвета. На выходе получается `"blue green"`.
|
|
|
|
|
- Оболочка слева успевает выполнить оба своих `echo` до того, как выполнится `echo blue`. На выходе получается `"green blue"`.
|
|
|
|
|
- Выполняется `echo blue` и выходит, закрывая pipe, до того, как завершится `echo red`. Оболочка в левой части конвейера записывает вывод в закрытый pipe, получает SIGPIPE и выходит из неё, не продолжая выполнять `echo green`. На выходе получается `"blue"`.
|
|
|
|
|
|
|
|
|
|
Команда `echo red` выполняется благодаря наличию буферизации. Так как ничто не читает её вывод `STDOUT`, то при заполнении буфера процесс `echo red` заблокируется. В случае отстуствия буфера `echo red; echo green 1>&2` бы не выполнился.
|
|
|
|
|
|
|
|
|
|
Из `man pipe`: "since Linux 2.6.11, the pipe capacity has been increased to 16 pages, which translates to 65,536 bytes on systems with a page size of 4096 bytes."
|
|
|
|
|
```
|
|
|
|
|
(i=1; while true; do echo red; echo $i 1>&2; ((i+=1)); done) | sleep 1
|
|
|
|
|
```
|
|
|
|
|
Вывод остановится на значении 16384 = 65536 / 4, т.к. "red\n" равен 4 байта. Вызвав `echo` только с переносом строки вместо `echo red` мы получим значение `65536`.
|
|
|
|
|
|
|
|
|
|
Таким образом, всё сводится к трём основным аспектам работы конвейров:
|
|
|
|
|
- Обе стороны выполняются одновременно в собственных процессах.
|
|
|
|
|
- Благодаря буферизации запись в pipe не блокируется мгновенно.
|
|
|
|
|
- Когда правая часть конвейра завершается, оболочка убивает левую часть.
|
|
|
|
|
|
|
|
|
|
## 2
|
|
|
|
|
Напишите .sh скрипт в котором в переменную date присваивается значение текущей даты, в переменную claim - строка "snow is white". После вызова скрипт должен выводить строку, используя переменные date и claim:
|
|
|
|
|
```
|
|
|
|
|
Ср 14 сен 2022 14:50:46 +04: The claim that “snow is white” is true if and only if snow is white.
|
|
|
|
|
```
|
|
|
|
|
Сделайте файл исполняемым для владельца и вызовите указанием полного или относительного пути к нему.
|
|
|
|
|
|
|
|
|
|
содержимое скрипта 2.sh
|
|
|
|
|
```bash
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
date=$(date)
|
|
|
|
|
claim='snow is white'
|
|
|
|
|
echo "$date: The claim that \"$claim\" is true if and only if $claim."
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ chmod u+x 2.sh
|
|
|
|
|
stud@stud12:~$ ./2.sh
|
|
|
|
|
Tue Oct 31 08:19:58 AM EDT 2023: The claim that "snow is white" is true if and only if snow is white.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 3
|
|
|
|
|
Напишите скрипт, который создает каталог, а затем переходит в него, создаёт в нём файл и записывает в него значения всех специальных переменных. Путь к каталогу должен быть передан в качестве первого аргумента.
|
|
|
|
|
```bash
|
|
|
|
|
stud@stud12:~$ cat 3.sh
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
path_to_dir=$1
|
|
|
|
|
vars=('$0' '$1' '$2' '$#' '$?' '$$' '$_' '$@' '$RANDOM' '$HOME' '$USER')
|
|
|
|
|
|
|
|
|
|
mkdir $path_to_dir
|
|
|
|
|
cd $path_to_dir
|
|
|
|
|
for cmd in ${vars[@]}; do
|
|
|
|
|
echo ${cmd}: $(eval echo $cmd) | tee -a all_vars;
|
|
|
|
|
done
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ ./3.sh test
|
|
|
|
|
$0: ./3.sh
|
|
|
|
|
$1: test
|
|
|
|
|
$2:
|
|
|
|
|
$#: 1
|
|
|
|
|
$?: 0
|
|
|
|
|
$$: 89872
|
|
|
|
|
$_: test
|
|
|
|
|
$@: test
|
|
|
|
|
$RANDOM: 29923
|
|
|
|
|
$HOME: /home/stud/
|
|
|
|
|
$USER: stud
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 4
|
|
|
|
|
Напишите аналогичный предыдущему заданию скрипт, но оберните все действия в функцию, а в конце скрипта выполните эту функцию. Проверьте работу скрипта.
|
|
|
|
|
```bash
|
|
|
|
|
stud@stud12:~$ cat 4.sh
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
f () {
|
|
|
|
|
path_to_dir=$1
|
|
|
|
|
vars=('$0' '$1' '$2' '$#' '$?' '$$' '$_' '$@' '$RANDOM' '$HOME' '$USER')
|
|
|
|
|
|
|
|
|
|
mkdir $path_to_dir
|
|
|
|
|
cd $path_to_dir
|
|
|
|
|
for cmd in ${vars[@]}; do
|
|
|
|
|
echo ${cmd}: $(eval echo $cmd) >> all_vars;
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f $1
|
|
|
|
|
```
|
|
|
|
|
Заметим, что при интерпретации команды в `eval` используются глобальные значения специальных переменных скрипта, а не функции.
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ cat test2/all_vars
|
|
|
|
|
$0: ./4.sh
|
|
|
|
|
$1: test2
|
|
|
|
|
$2:
|
|
|
|
|
$#: 1
|
|
|
|
|
$?: 0
|
|
|
|
|
$$: 90118
|
|
|
|
|
$_: 90118
|
|
|
|
|
$@: test2
|
|
|
|
|
$RANDOM: 14079
|
|
|
|
|
$HOME: /home/stud/
|
|
|
|
|
$USER: stud
|
|
|
|
|
```
|
|
|
|
|
Доступна ли функция за пределами контекста скрипта?
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ f
|
|
|
|
|
-bash: f: command not found
|
|
|
|
|
```
|
|
|
|
|
Заведите внутри функции глобальную `a` и локальную переменную `local b`. Вызовите функцию в скрипте. Выведите обе переменные на экран внутри функции после присвоения значений переменным и после вызова функции.
|
|
|
|
|
```bash
|
|
|
|
|
stud@stud12:~$ cat 4_2.sh
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
a='global a'
|
|
|
|
|
f () {
|
|
|
|
|
local b='local b'
|
|
|
|
|
echo a: ${a}, b: ${b}
|
|
|
|
|
}
|
|
|
|
|
echo a: ${a}, b: ${b}
|
|
|
|
|
f
|
|
|
|
|
echo a: ${a}, b: ${b}
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ ./4_2.sh
|
|
|
|
|
a: global a, b:
|
|
|
|
|
a: global a, b: local b
|
|
|
|
|
a: global a, b:
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 5.
|
|
|
|
|
Функции выполняются в текущей среде оболочке, тогда как скрипты выполняются в своем собственном процессе.
|
|
|
|
|
|
|
|
|
|
Проверьте, что функции могут изменять переменные среды, например, изменить текущий каталог, тогда как скрипт не может. Напишите скрипт, в котором инициализируются две переменные A и B. Перед одной из них указывается export. Выполните скрипт и посмотрите на выдачу команды env. Выполните файл командой source (документация в source --help и секции встроенных команд man bash).
|
|
|
|
|
```bash
|
|
|
|
|
$ cat 8.sh
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
A=1
|
|
|
|
|
export B=2
|
|
|
|
|
```
|
|
|
|
|
Какие переменные экспортировались в оболочку?
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ ./8.sh
|
|
|
|
|
stud@stud12:~$ env | grep -E "^A|B"
|
|
|
|
|
stud@stud12:~$ source ./8.sh
|
|
|
|
|
stud@stud12:~$ env | grep -E "^A|B"
|
|
|
|
|
B=2
|
|
|
|
|
```
|
|
|
|
|
Экспортировалась `B`.
|
|
|
|
|
|
|
|
|
|
## 6.
|
|
|
|
|
Попробуйте условные выражения в скрипте в if конструкции.
|
|
|
|
|
```bash
|
|
|
|
|
$ cat 6.sh
|
|
|
|
|
if [[ true && true ]]
|
|
|
|
|
then
|
|
|
|
|
echo "True"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ true || false ]]
|
|
|
|
|
then
|
|
|
|
|
echo "True"
|
|
|
|
|
fi
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ ./6.sh
|
|
|
|
|
True
|
|
|
|
|
True
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 6.5
|
|
|
|
|
|
|
|
|
|
## 7.
|
|
|
|
|
Напишите скрипт, который проходит по всем файлам, переданным в качестве аргументов, и ищет в них строку `hello`. Перенаправьте `grep` `STDOUT` и `STDERR` в специальный файл `/dev/null`. Для каждого файла в `if` создайте файл с содержимым `hello`, если `grep` завершился с ошибкой.
|
|
|
|
|
```bash
|
|
|
|
|
$ cat 7.sh
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
for f in $@; do
|
|
|
|
|
grep hello $f 2> /dev/null 1> /dev/null
|
|
|
|
|
if [[ $? -ne 0 ]]
|
|
|
|
|
then
|
|
|
|
|
echo $f has no hello in it. correcting.
|
|
|
|
|
echo "hello" > $f
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:/home/stud$ ./7.sh /tmp/one /tmp/two /tmp/three
|
|
|
|
|
/tmp/one has no hello in it. correcting.
|
|
|
|
|
/tmp/two has no hello in it. correcting.
|
|
|
|
|
/tmp/three has no hello in it. correcting.
|
|
|
|
|
|
|
|
|
|
stud@stud12:/home/stud$ ./7.sh /tmp/one /tmp/two /tmp/three
|
|
|
|
|
|
|
|
|
|
stud@stud12:/home/stud$ grep hello /tmp/* 2> /dev/null
|
|
|
|
|
/tmp/one:hello
|
|
|
|
|
/tmp/three:hello
|
|
|
|
|
/tmp/two:hello
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 8.
|
|
|
|
|
Установите пакет shellcheck и проверьте написанный вами ранее скрипт одноименной командой.
|
|
|
|
|
```bash
|
|
|
|
|
stud@stud12:/home/stud$ shellcheck 7.sh
|
|
|
|
|
|
|
|
|
|
In 7.sh line 2:
|
|
|
|
|
for f in $@; do
|
|
|
|
|
^-- SC2068 (error): Double quote array expansions to avoid re-splitting elements.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In 7.sh line 3:
|
|
|
|
|
grep hello $f 2> /dev/null 1> /dev/null
|
|
|
|
|
^-- SC2086 (info): Double quote to prevent globbing and word splitting.
|
|
|
|
|
|
|
|
|
|
Did you mean:
|
|
|
|
|
grep hello "$f" 2> /dev/null 1> /dev/null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In 7.sh line 4:
|
|
|
|
|
if [[ $? -ne 0 ]]
|
|
|
|
|
^-- SC2181 (style): Check exit code directly with e.g. 'if ! mycmd;', not indirectly with $?.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In 7.sh line 6:
|
|
|
|
|
echo $f has no hello in it. correcting.
|
|
|
|
|
^-- SC2086 (info): Double quote to prevent globbing and word splitting.
|
|
|
|
|
|
|
|
|
|
Did you mean:
|
|
|
|
|
echo "$f" has no hello in it. correcting.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In 7.sh line 7:
|
|
|
|
|
echo "hello" > $f
|
|
|
|
|
^-- SC2086 (info): Double quote to prevent globbing and word splitting.
|
|
|
|
|
|
|
|
|
|
Did you mean:
|
|
|
|
|
echo "hello" > "$f"
|
|
|
|
|
|
|
|
|
|
For more information:
|
|
|
|
|
https://www.shellcheck.net/wiki/SC2068 -- Double quote array expansions to ...
|
|
|
|
|
https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...
|
|
|
|
|
https://www.shellcheck.net/wiki/SC2181 -- Check exit code directly with e.g...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 9.
|
|
|
|
|
|
|
|
|
|
### 9.1
|
|
|
|
|
Почему в этом примере не работает печать?
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -e
|
|
|
|
|
i=0
|
|
|
|
|
let i++
|
|
|
|
|
echo "i is $i"
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:/home/stud$ bash 9_1.sh
|
|
|
|
|
```
|
|
|
|
|
Согласно руководству, `set -e` завершает работу, если простая команда завершается с ненулевым статусом. Оболочка не завершает работу, если команда, которая завершилась неудачей, является частью списка команд, следующего сразу за ключевым словом `while` или `until`, частью проверки в операторе `if`, частью списка `&&` или `||`, или если возвращаемое значение команды инвертируется через `!`.
|
|
|
|
|
|
|
|
|
|
Команда `let` - это простая команда, и она не попадает ни под одно из исключений из приведенного выше списка. Более того, `help let` сообщает нам: "Если последний ARG вычисляется в `0`, `let` возвращает `1`; в противном случае возвращается `0`". `i++` равен `0`, поэтому `let i++` возвращает `1` и срабатывает `set -e`. Скрипт прерывается. Потому что мы добавили `1` в переменную.
|
|
|
|
|
|
|
|
|
|
### 9.2
|
|
|
|
|
Почему этот вариант иногда работает? В каких версиях bash он работает, а в каких - нет?
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -e
|
|
|
|
|
i=0
|
|
|
|
|
((i++))
|
|
|
|
|
echo "i is $i"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`((...))` не является простой командой в соответствии с грамматикой оболочки (`man --pager='less -p ^SHELL GRAMMAR' bash`). Поэтому она не может вызвать прерывание `set -e`, даже если в данном конкретном случае возвращает `1` (поскольку при установке `i` в `1` командой `i++` кодом возврата считается `0`, а `0` в математическом контексте считается ложью).
|
|
|
|
|
|
|
|
|
|
Однако в bash 4.1 это поведение изменилось. Упражнение 2 работает только в bash 4.0 и более ранних версиях! В bash 4.1 `((...))` квалифицируется как `set -e abortion`, и это упражнение ничего не выведет, как и упражнение 1.
|
|
|
|
|
|
|
|
|
|
Вы не можете рассчитывать на то, что он будет вести себя последовательно в разных выпусках оболочки.
|
|
|
|
|
|
|
|
|
|
### 9.3
|
|
|
|
|
Почему эти два сценария не идентичны?
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -e
|
|
|
|
|
test -d nosuchdir && echo no dir
|
|
|
|
|
echo survived
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -e
|
|
|
|
|
f() { test -d nosuchdir && echo no dir; }
|
|
|
|
|
f
|
|
|
|
|
echo survived
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:/home/stud$ bash 9_3_a.sh
|
|
|
|
|
survived
|
|
|
|
|
stud@stud12:/home/stud$ bash 9_3_b.sh
|
|
|
|
|
stud@stud12:/home/stud$
|
|
|
|
|
```
|
|
|
|
|
В первом скрипте тестовая команда является частью любой команды, выполняемой в списке `&&` или `||`, кроме команды, следующей за последней `&&` или `||` (man page Bash 4.2), поэтому она не приводит к выходу оболочки.
|
|
|
|
|
|
|
|
|
|
Во втором сценарии это также верно, поэтому оболочка не выходит сразу после команды `test ... &&`. Однако функция `fn` возвращает `1` (отказ), поскольку таков был статус выхода последней команды, выполненной в функции. Поэтому простая команда `fn` в основном теле скрипта возвращает `1` (отказ), что приводит к выходу оболочки.
|
|
|
|
|
|
|
|
|
|
## 9.4
|
|
|
|
|
Почему эти два сценария не идентичны?
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -e
|
|
|
|
|
f() { test -d nosuchdir && echo no dir; }
|
|
|
|
|
f
|
|
|
|
|
echo survived
|
|
|
|
|
```
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -e
|
|
|
|
|
f() { if test -d nosuchdir; then echo no dir; fi; }
|
|
|
|
|
f
|
|
|
|
|
echo survived
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:/home/stud$ bash 9_4_a.sh
|
|
|
|
|
stud@stud12:/home/stud$ bash 9_4_b.sh
|
|
|
|
|
survived
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Первый скрипт, приведенный выше, аналогичен второму скрипту из упражнения 3.
|
|
|
|
|
|
|
|
|
|
Во втором скрипте мы наблюдаем один из вариантов, в котором `if` и `&&` не являются одним и тем же. В руководстве, в разделе Compound Commands, мы находим это предложение в определении `if`:
|
|
|
|
|
|
|
|
|
|
Статус выхода - это статус выхода последней выполненной команды или ноль, если ни одно условие не было выполнено.
|
|
|
|
|
|
|
|
|
|
Поскольку термины "команда" и "условие" в man-странице особо не различаются, то, следуя первой части этого объяснения из `man bash`, может показаться, что статус выхода из структуры `if` должен быть статусом выхода тестовой команды, однако в данном случае тестовая команда является условием логической структуры `if`, поэтому применима вторая часть приведенного выше объяснения. Если `test` или любой список команд в условной позиции логической структуры возвращает код выхода, не равный `0`, и при этом нет ветвей `elif` или `else`, то код выхода всей структуры `if` по-прежнему равен `0`.
|
|
|
|
|
|
|
|
|
|
В более ясной формулировке, поскольку тест не является истинным и никакие последующие команды не выполняются, `if` должна возвращать `0`. Это означает, что `fn` возвращает `0`, и оболочка не завершается.
|
|
|
|
|
|
|
|
|
|
### 9.5
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -e
|
|
|
|
|
read -r foo < configfile
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:/home/stud$ bash 9_5.sh
|
|
|
|
|
9_5.sh: line 3: configfile: No such file or directory
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Очевидно, что в случае отсутствия или нечитаемости файла `configfile` работа будет прервана. Он также прервется (возможно, неожиданно), если в файле отсутствует завершающая новая строка. Это происходит потому, что `read` возвращает код ошибки, если он достиг конца файла до прочтения ожидаемой новой строки. Однако содержимое файла все равно будет прочитано, и переменная будет заполнена.
|
|
|
|
|
|
|
|
|
|
Если бы не было `set -e`, скрипт правильно заполнил бы переменную и пошел дальше, и тот факт, что файл "неполный", не стал бы проблемой. Заметим, что использование списка `while` или `if` с `read` приведет к заполнению переменной в любом случае, поскольку оба варианта являются исключениями из правил `set -e`.
|
|
|
|
|
|
|
|
|
|
Рекомендация проста: не используйте `set -e`. Вместо этого добавьте собственную проверку ошибок.
|
|
|
|
|
|
|
|
|
|
## 10.
|
|
|
|
|
Напишите скрипт, который выполняет следующий код:
|
|
|
|
|
```python
|
|
|
|
|
$ cat ./7.sh
|
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
import sys
|
|
|
|
|
for arg in reversed(sys.argv[1:]):
|
|
|
|
|
print(arg)
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
$ ./7.sh 1 2 3 4 5
|
|
|
|
|
5
|
|
|
|
|
4
|
|
|
|
|
3
|
|
|
|
|
2
|
|
|
|
|
1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 11.
|
|
|
|
|
Скажем, у вас есть команда, которая редко дает сбой. Чтобы отладить её, вам нужно зафиксировать её выходные данные, но это может занять много времени, чтобы поймать неудачный запуск. Напишите сценарий bash, который запускает следующий сценарий до тех пор, пока он не завершится аварийно, записывает стандартный вывод и потоки ошибок в файлы и печатает все в конце. Бонусный балл, за то, что вы можете сообщить, сколько запусков потребовалось для сбоя сценария.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
|
|
|
|
n=$(( RANDOM % 100 ))
|
|
|
|
|
|
|
|
|
|
if [[ n -eq 42 ]]; then
|
|
|
|
|
echo "Something went wrong"
|
|
|
|
|
>&2 echo "The error was using magic numbers"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo "Everything went according to plan"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
stud@stud12:~$ cat repeat_until_error.sh
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
echo "" > output.log
|
|
|
|
|
echo "" > error.log
|
|
|
|
|
i=0
|
|
|
|
|
while true; do
|
|
|
|
|
bash $1 2>> error.log 1>> output.log
|
|
|
|
|
if [[ $? -ne 0 ]]; then
|
|
|
|
|
echo Took "$i" iterations to get an error.
|
|
|
|
|
echo "\noutput.log"
|
|
|
|
|
cat output.log
|
|
|
|
|
echo "\nerror.log"
|
|
|
|
|
cat error.log
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
i=$((i+1))
|
|
|
|
|
done
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
stud@stud12:~$ ./repeat_until_error.sh ./random_bug_script.sh
|
|
|
|
|
Took 7 iterations to get an error.
|
|
|
|
|
\noutput.log
|
|
|
|
|
|
|
|
|
|
Everything went according to plan
|
|
|
|
|
Everything went according to plan
|
|
|
|
|
Everything went according to plan
|
|
|
|
|
Everything went according to plan
|
|
|
|
|
Everything went according to plan
|
|
|
|
|
Everything went according to plan
|
|
|
|
|
Everything went according to plan
|
|
|
|
|
Something went wrong
|
|
|
|
|
\nerror.log
|
|
|
|
|
|
|
|
|
|
The error was using magic numbers
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 12.
|
|
|
|
|
|
|
|
|
|
Настройка беспарольного перехода для суперпользователя между машинами.
|
|
|
|
|
```
|
|
|
|
|
root@stud12:~# cat ./.ssh/config
|
|
|
|
|
Host w001
|
|
|
|
|
HostName 10.160.179.22
|
|
|
|
|
|
|
|
|
|
Host w002
|
|
|
|
|
HostName 10.160.179.42
|
|
|
|
|
|
|
|
|
|
root@stud12:~# ip a show ens18 | grep inet
|
|
|
|
|
inet 10.160.179.22/24 brd 10.160.179.255 scope global ens18
|
|
|
|
|
inet6 fe80::5871:c6ff:fe12:f553/64 scope link
|
|
|
|
|
|
|
|
|
|
root@stud12:~# ssh w002 "ip a show ens18 | grep inet"
|
|
|
|
|
root@10.160.179.42's password:
|
|
|
|
|
inet 10.160.179.42/24 brd 10.160.179.255 scope global ens18
|
|
|
|
|
inet6 fe80::28d4:acff:fe73:5b9/64 scope link
|
|
|
|
|
|
|
|
|
|
root@stud12:~# ssh w002 "cat /etc/ssh/sshd_config | grep PermitRootLogin "
|
|
|
|
|
root@10.160.179.42's password:
|
|
|
|
|
# PermitRootLogin prohibit-password
|
|
|
|
|
PermitRootLogin yes
|
|
|
|
|
# the setting of "PermitRootLogin prohibit-password".
|
|
|
|
|
|
|
|
|
|
root@stud12:~# ssh-copy-id w002
|
|
|
|
|
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
|
|
|
|
|
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
|
|
|
|
|
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
|
|
|
|
|
root@10.160.179.42's password:
|
|
|
|
|
|
|
|
|
|
Number of key(s) added: 1
|
|
|
|
|
|
|
|
|
|
Now try logging into the machine, with: "ssh 'w002'"
|
|
|
|
|
and check to make sure that only the key(s) you wanted were added.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
root@stud12:~# ssh w002 "cat /etc/ssh/sshd_config | egrep '^PasswordAuthentication|^PermitRootLogin'"
|
|
|
|
|
PermitRootLogin prohibit-password
|
|
|
|
|
PasswordAuthentication no
|
|
|
|
|
```
|
|
|
|
|
Скрипт
|
|
|
|
|
```bash
|
|
|
|
|
root@stud12:~# cat create_user.sh
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
help() {
|
|
|
|
|
echo "Usage: ./create_user.sh file_with_node_ip_addresses user_name"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if [[ $# < 2 ]]
|
|
|
|
|
then
|
|
|
|
|
help
|
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nodes=$(cat $1)
|
|
|
|
|
user=$2
|
|
|
|
|
create_user_and_link_command="useradd $user --create-home --shell=/bin/bash --key HOME_MODE=0700; \
|
|
|
|
|
mkdir -p /ceph/$user -m 0700; \
|
|
|
|
|
chown $user:$user /ceph/$user; \
|
|
|
|
|
ln -s /ceph/$user /home/$user/ceph; \
|
|
|
|
|
chown $user:$user --no-dereference /home/$user/ceph"
|
|
|
|
|
|
|
|
|
|
mkdir -p /root/users.ssh/$user/
|
|
|
|
|
ssh-keygen -t ed25519 -N "" -f "/root/users.ssh/$user/id_ed25519"
|
|
|
|
|
sed --in-place "s/root/user/" "/root/users.ssh/$user/id_ed25519.pub"
|
|
|
|
|
cat /root/users.ssh/$user/id_ed25519.pub > /root/users.ssh/$user/authorized_keys
|
|
|
|
|
|
|
|
|
|
echo "" > /root/users.ssh/$user/known_hosts
|
|
|
|
|
for node in $nodes; do
|
|
|
|
|
ssh-keyscan -H "$node" >> /root/users.ssh/$user/known_hosts
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
for node in $nodes; do
|
|
|
|
|
echo "$node" "$create_user_and_link_command"
|
|
|
|
|
ssh "$node" "$create_user_and_link_command"
|
|
|
|
|
scp -r /root/users.ssh/$user/ $node:/home/$user/.ssh
|
|
|
|
|
ssh "$node" "chown -R $user:$user /home/$user/.ssh && chmod 0700 /home/$user/.ssh/"
|
|
|
|
|
done
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
root@stud12:~# cat nodes
|
|
|
|
|
10.160.179.22
|
|
|
|
|
10.160.179.42
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Проверка
|
|
|
|
|
```
|
|
|
|
|
root@stud12:~# ./create_user.sh nodes user22
|
|
|
|
|
Generating public/private ed25519 key pair.
|
|
|
|
|
Your identification has been saved in /root/users.ssh/user22/id_ed25519
|
|
|
|
|
Your public key has been saved in /root/users.ssh/user22/id_ed25519.pub
|
|
|
|
|
The key fingerprint is:
|
|
|
|
|
SHA256:qz7py+1prq6iQ2YO2pmSoXBiujCVVKhnmqi6LvuA/gM root@stud12
|
|
|
|
|
The key's randomart image is:
|
|
|
|
|
+--[ED25519 256]--+
|
|
|
|
|
| .. |
|
|
|
|
|
| .. |
|
|
|
|
|
| .. |
|
|
|
|
|
|..o. |
|
|
|
|
|
|.=o S |
|
|
|
|
|
|XE. . |
|
|
|
|
|
|^*.o .. |
|
|
|
|
|
|%+=. .oo.. |
|
|
|
|
|
|X@ooo=OB= |
|
|
|
|
|
+----[SHA256]-----+
|
|
|
|
|
# 10.160.179.22:22 SSH-2.0-OpenSSH_9.2p1 Debian-2
|
|
|
|
|
# 10.160.179.22:22 SSH-2.0-OpenSSH_9.2p1 Debian-2
|
|
|
|
|
# 10.160.179.22:22 SSH-2.0-OpenSSH_9.2p1 Debian-2
|
|
|
|
|
# 10.160.179.22:22 SSH-2.0-OpenSSH_9.2p1 Debian-2
|
|
|
|
|
# 10.160.179.22:22 SSH-2.0-OpenSSH_9.2p1 Debian-2
|
|
|
|
|
# 10.160.179.42:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u1
|
|
|
|
|
# 10.160.179.42:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u1
|
|
|
|
|
# 10.160.179.42:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u1
|
|
|
|
|
# 10.160.179.42:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u1
|
|
|
|
|
# 10.160.179.42:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u1
|
|
|
|
|
10.160.179.22 useradd user22 --create-home --shell=/bin/bash --key HOME_MODE=0700; mkdir -p /ceph/user22 -m 0700; chown user22:user22 /ceph/user22; ln -s /ceph/user22 /home/user22/ceph; chown user22:user22 --no-dereference /home/user22/ceph
|
|
|
|
|
useradd: user 'user22' already exists
|
|
|
|
|
known_hosts 100% 1957 6.2MB/s 00:00
|
|
|
|
|
id_ed25519.pub 100% 93 431.8KB/s 00:00
|
|
|
|
|
id_ed25519 100% 399 1.6MB/s 00:00
|
|
|
|
|
authorized_keys 100% 93 503.8KB/s 00:00
|
|
|
|
|
10.160.179.42 useradd user22 --create-home --shell=/bin/bash --key HOME_MODE=0700; mkdir -p /ceph/user22 -m 0700; chown user22:user22 /ceph/user22; ln -s /ceph/user22 /home/user22/ceph; chown user22:user22 --no-dereference /home/user22/ceph
|
|
|
|
|
useradd: user 'user22' already exists
|
|
|
|
|
known_hosts 100% 1957 4.9MB/s 00:00
|
|
|
|
|
id_ed25519.pub 100% 93 356.6KB/s 00:00
|
|
|
|
|
id_ed25519 100% 399 1.4MB/s 00:00
|
|
|
|
|
authorized_keys 100% 93 384.4KB/s 00:00
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
root@stud12:~# su - user22
|
|
|
|
|
user22@stud12:~$ ssh 10.160.179.42
|
|
|
|
|
The authenticity of host '10.160.179.42 (10.160.179.42)' can't be established.
|
|
|
|
|
ED25519 key fingerprint is SHA256:u9oSjb/BWxCt6BmjdCGf57l9Fp1wvSl8X6Ip3nHYCSU.
|
|
|
|
|
This key is not known by any other names.
|
|
|
|
|
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
|
|
|
|
|
Warning: Permanently added '10.160.179.42' (ED25519) to the list of known hosts.
|
|
|
|
|
Linux debian 6.1.0-13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.55-1 (2023-09-29) x86_64
|
|
|
|
|
|
|
|
|
|
The programs included with the Debian GNU/Linux system are free software;
|
|
|
|
|
the exact distribution terms for each program are described in the
|
|
|
|
|
individual files in /usr/share/doc/*/copyright.
|
|
|
|
|
|
|
|
|
|
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
|
|
|
|
|
permitted by applicable law.
|
|
|
|
|
user22@debian:~$ ssh 10.160.179.22
|
|
|
|
|
The authenticity of host '10.160.179.22 (10.160.179.22)' can't be established.
|
|
|
|
|
ED25519 key fingerprint is SHA256:rqN99l7uB8lGIeqGb4wEa/BIhlZBUPMF2pGLYQem9qI.
|
|
|
|
|
This key is not known by any other names.
|
|
|
|
|
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
|
|
|
|
|
Warning: Permanently added '10.160.179.22' (ED25519) to the list of known hosts.
|
|
|
|
|
Linux stud12 6.1.0-12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.52-1 (2023-09-07) x86_64
|
|
|
|
|
|
|
|
|
|
The programs included with the Debian GNU/Linux system are free software;
|
|
|
|
|
the exact distribution terms for each program are described in the
|
|
|
|
|
individual files in /usr/share/doc/*/copyright.
|
|
|
|
|
|
|
|
|
|
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
|
|
|
|
|
permitted by applicable law.
|
|
|
|
|
user22@stud12:~$
|
|
|
|
|
logout
|
|
|
|
|
Connection to 10.160.179.22 closed.
|
|
|
|
|
user22@debian:~$
|
|
|
|
|
logout
|
|
|
|
|
Connection to 10.160.179.42 closed.
|
|
|
|
|
```
|