Форум русскоязычного сообщества Ubuntu


Считаете, что Ubuntu недостаточно дружелюбна к новичкам?
Помогите создать новое Руководство для новичков!

Автор Тема: Трюки и интересные задания по Bash для всех желающих  (Прочитано 906 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Malamut

  • Автор темы
  • Ubuntu Member
  • Администратор
  • Старожил
  • *
  • Сообщений: 3353
  • Я добрый, честно!
    • Просмотр профиля
    • Моя страница на Launchpad
Я в течение многих последних лет вынужденно читаю ответы на всякие вопросы "а как в Bash сделать то-то" на разных ресурсах. А так же целые статьи от людей, которые наивно думают, что они постигли дзен и потому могут передать тайные знания неограниченному кругу юных падаванов :coolsmiley:

Как вы поняли - просто катастрофически огромное количество подобных советов мягко говоря безграмотно. И следование им приведёт вас к тому, что ваш классный скрипт бекапа в одночасье снесёт вам полсистемы, когда вы случайно создадите файл с пробелом или ; в имени.

Так вот. Предлагаю в этой теме публиковать каверзные минизадания (но только мини!) тем, кто встречается с какими-то интересными штуками в процессе работы с башом, а всем заинтересованным - писать на них ответы. Но главное -критиковать ответы других, пытаясь их сломать. Плюс при ответе писать комментарии к местам, в которых недостаточно сведущие в баше люди могут совершить ошибку.

В итоге, я надеюсь, в ходе общения в этой теме желающие узнают много нового про то, что и как можно делать в баше, а так же про то, что они всю жизнь делали неправильно ;)
"Носителем суверенитета и единственным источником власти в Российской Федерации является ее многонациональный народ" Конституция РФ

Оффлайн Malamut

  • Автор темы
  • Ubuntu Member
  • Администратор
  • Старожил
  • *
  • Сообщений: 3353
  • Я добрый, честно!
    • Просмотр профиля
    • Моя страница на Launchpad
Начну с простого задания, подобное которому все сисадмины решали множество раз.

Задание 1: Как в баше разделить входные агрументы скрипта на два массива - в первом должны остаться аргументы, начинающиеся на '-', а во втором - все остальные.

Предлагайте варианты) Критикуйте ответы других, если видите в них какие-то проблемы.
« Последнее редактирование: 26 Сентябрь 2020, 00:52:01 от Malamut »
"Носителем суверенитета и единственным источником власти в Российской Федерации является ее многонациональный народ" Конституция РФ

Оффлайн ALiEN175

  • Модератор форума
  • Старожил
  • *
  • Сообщений: 4708
  • Capture the truth
    • Просмотр профиля
Хм, попробую)

Код: (bash) [Выделить]
#!/bin/bash

while [ "$1" ] ; do
  case "$1" in
    -*) MINUS=$1\ $MINUS ;;
    *) NOTMINUS=$1\ $NOTMINUS ;;
  esac
  shift
  continue
done

echo "МИНУСЫ: $MINUS"
echo "НЕМИНУСЫ: $NOTMINUS"
ASUS P5K-C :: Intel Xeon E5450 :: 8 GB RAM :: Nvidia 8500GT :: XFCE
SAMSUNG N150 :: Intel Atom N450 :: 2 GB RAM :: Intel GMA3150 :: XFCE

Оффлайн valrust

  • Активист
  • *
  • Сообщений: 285
    • Просмотр профиля
Как в баше разделить входные агрументы скрипта на два массива - в первом должны остаться аргументы, начинающиеся на '-', а во втором - все остальные.
Мой вариант

Код: (bash) [Выделить]
#! /usr/bin/bash

# Объявляем два массива
declare -a a
declare -a b

# Начинаем перебирать все параметры скрипта
for param in "$@"
do
        # Каждый параметр проверям
        case "$param" in
                # есть ли у него в начале -
                -*) a+=($param) ;; # если есть -, то добавляем к массиву a
                *)  b+=($param) ;; # если нет, то добавляем к массиву b
        esac
done

# Выводим массимы на стандартный выход
echo "${a[*]}"
echo "${b[*]}"

Оффлайн Malamut

  • Автор темы
  • Ubuntu Member
  • Администратор
  • Старожил
  • *
  • Сообщений: 3353
  • Я добрый, честно!
    • Просмотр профиля
    • Моя страница на Launchpad
Класс!) Скажу пока что, что оба варианта легко ломаются тривиальным способом:

Код: (bash) [Выделить]
test.sh 'вы забыли про пробелы'
test.sh 'вы' 'забыли' 'про' 'пробелы'

Правда, из-за специфики вывода на экран в варианте valrust будет незаметно, что оно сломалось, но тем не менее) В остальном вариант valrust более-менее ок, а вот в варианте ALiEN175 есть одна супер нетривиальная проблема. Ну, кроме того что надо было использовать массивы)) Никогда не сохраняйте множественные значения в строковые переменные, если можете этого не делать - массивы значительно упрощают жизнь и избавляют от проблем с тем, что, например, в ваших переменных могут быть пробелы) И continue последней операцией цикла не нужен, конечно, оно и так там сделает continue, это ж цикл.

Про нетривиальную проблему напишу позже, если её никто другой не найдёт.
"Носителем суверенитета и единственным источником власти в Российской Федерации является ее многонациональный народ" Конституция РФ

Оффлайн Malamut

  • Автор темы
  • Ubuntu Member
  • Администратор
  • Старожил
  • *
  • Сообщений: 3353
  • Я добрый, честно!
    • Просмотр профиля
    • Моя страница на Launchpad
Пока расширение задания, чтоб цикл for уже не так легко прокатывал. Другие варианты решения задания 1 тоже принимаются и будут приниматься дальше, особенно какие-нибудь интересные)

Задание 1.2: Нужно всё ещё разделить аргументы на два массива. Но первый должен быть ассоциативным, и в него должны попадать значения для однобуквенных опций скрипта. Например, агрументы скрипта -n test должны приводить к появлению элемента массива вида
OPTS['n']='test'
Список возможных опций известен, и не все поддерживают значения. Если опция не требует значения - в ассоциативный массив надо записать 1 при её появлении. Пускай такие опции:
-h - не требует значения
-v - не требует значения
-n - требует
-s - тоже требует
Все прочие аргументы скрипта должны попадать во второй, обычный, массив. Такой этакий самопальный парсинг опций на чистом баше)
"Носителем суверенитета и единственным источником власти в Российской Федерации является ее многонациональный народ" Конституция РФ

Оффлайн ALiEN175

  • Модератор форума
  • Старожил
  • *
  • Сообщений: 4708
  • Capture the truth
    • Просмотр профиля
Цитировать
Скажу пока что, что оба варианта легко ломаются тривиальным способом:
c массивами bash не очень-то знаком. :-\ Выкатил просто в строку, через пробел.
А с пробелами в аргументах всё должно быть ОК
$: ./test -'тут пробелы' more spaces 'вы забыли' '1 2 3' 3 2 1
МИНУСЫ: -тут пробелы
НЕМИНУСЫ: 1 2 3 1 2 3 вы забыли spaces more
* обратить внимание на порядок слов после скрипта в кавычках и без них
ASUS P5K-C :: Intel Xeon E5450 :: 8 GB RAM :: Nvidia 8500GT :: XFCE
SAMSUNG N150 :: Intel Atom N450 :: 2 GB RAM :: Intel GMA3150 :: XFCE

Оффлайн Malamut

  • Автор темы
  • Ubuntu Member
  • Администратор
  • Старожил
  • *
  • Сообщений: 3353
  • Я добрый, честно!
    • Просмотр профиля
    • Моя страница на Launchpad
c массивами bash не очень-то знаком
Вот и будем знакомиться)) Без массивов практически невозможно написать скрипты, которые нормально работают с какими-либо списками (списками файлов, опций, котиков и чего угодно ещё)
А с пробелами в аргументах всё должно быть ОК
Всё конечно ок) Только как вы теперь разделите $NOTMINUS обратно на аргументы?)) Чтоб, например, пройтись по ним циклом и что-то с каждым сделать. Пусть хотя бы вывести на экран в "кавычках" вида "<<значение аргумента>>", по одному аргументу на строчку.

P.S. использовать обычные или ассоциативные массивы в баше элементарно. Оба этих типа поддерживают фактически всего 3 операции - запись значения по ключу, итерацию по массиву (или по ключам массива), ну и добавление элементов через ARR+=(). Гугланите bash arrays и bash associative arrays и научитесь их использовать буквально за полчасика.
« Последнее редактирование: 26 Сентябрь 2020, 01:24:43 от Malamut »
"Носителем суверенитета и единственным источником власти в Российской Федерации является ее многонациональный народ" Конституция РФ

Оффлайн valrust

  • Активист
  • *
  • Сообщений: 285
    • Просмотр профиля
Класс!) Скажу пока что, что оба варианта легко ломаются тривиальным способом:

Исправляюсь :)

Забыл в блоке case заключить в двойные кавычки $param. Еще добавил разделитель "/" для вывода элементов массива.
Код: (bash) [Выделить]
#! /usr/bin/bash

# Объявляем два массива
declare -a a
declare -a b
# Начинаем перебирать все параметры скрипта
for param in "$@"
do
        # Каждый параметр проверяем
        case "$param" in
                # есть ли у него в начале -
                -*) a+=("$param") ;; # если есть -, то добавляем к массиву a
                *)  b+=("$param") ;; # если нет, то добавляем к массиву b
        esac
done

# Выводим массивы на стандартный выход
IFS=/ # это разделитель для вывода элементов массива
echo "${a[*]}"
echo "${b[*]}"

Теперь с пробелами не должно  быть проблем.
(Нажмите, чтобы показать/скрыть)

Оффлайн Malamut

  • Автор темы
  • Ubuntu Member
  • Администратор
  • Старожил
  • *
  • Сообщений: 3353
  • Я добрый, честно!
    • Просмотр профиля
    • Моя страница на Launchpad
valrust,
Так правильно. Но забывать двойные кавычки вокруг переменных - это самая распространённая ошибка. И очень опасная) И да, IFS менять - решение неплохое, но надо понимать, что это меняет поведение всех команд ниже в скрипте. Поэтому делать такие штуки в реальных скриптах надо очень аккуратно. Но это так, ремарка.

Попробуйте сделать задание 1.2. Казалось бы, просто добавить больше случаев в case, но вот нет)
"Носителем суверенитета и единственным источником власти в Российской Федерации является ее многонациональный народ" Конституция РФ

Оффлайн ALiEN175

  • Модератор форума
  • Старожил
  • *
  • Сообщений: 4708
  • Capture the truth
    • Просмотр профиля
Без массивов практически невозможно написать скрипты, которые нормально работают с какими-либо списками (списками файлов, опций, котиков и чего угодно ещё)

(Нажмите, чтобы показать/скрыть)
ASUS P5K-C :: Intel Xeon E5450 :: 8 GB RAM :: Nvidia 8500GT :: XFCE
SAMSUNG N150 :: Intel Atom N450 :: 2 GB RAM :: Intel GMA3150 :: XFCE

Оффлайн Malamut

  • Автор темы
  • Ubuntu Member
  • Администратор
  • Старожил
  • *
  • Сообщений: 3353
  • Я добрый, честно!
    • Просмотр профиля
    • Моя страница на Launchpad
ALiEN175,
(Нажмите, чтобы показать/скрыть)
"Носителем суверенитета и единственным источником власти в Российской Федерации является ее многонациональный народ" Конституция РФ

Оффлайн Azure

  • Модератор раздела
  • Старожил
  • *
  • Сообщений: 6017
  • Windows10, i3wm on Debian9, Manjaro20.0
    • Просмотр профиля
для однобуквенных опций скрипта
ЕМНИП существует внутрення приблуда баша под названием getopts, которая и заниматеся отделением параметров от аргументов:
https://www.opennet.ru/docs/RUS/bash_scripting_guide/c5358.html#EX33
« Последнее редактирование: 28 Сентябрь 2020, 00:05:09 от Azure »
В Линукс можно сделать ВСЁ что угодно, достаточно знать КАК !

Оффлайн Malamut

  • Автор темы
  • Ubuntu Member
  • Администратор
  • Старожил
  • *
  • Сообщений: 3353
  • Я добрый, честно!
    • Просмотр профиля
    • Моя страница на Launchpad
getopts, который внутренний, бесполезен - он не умеет длинные опции. А gnu getopt, который внешняя утилита - это лишь проверяльщик, разбирать опции всё равно надо ручками. Ну и я задание 1.2 дал не для того, чтоб написать полноценный парсер опций, хотя это весьма и весьма интересная задача, а потому, что в подобных заданиях практически все, кто пишет что-то на баше, совершают дикие ошибки.
"Носителем суверенитета и единственным источником власти в Российской Федерации является ее многонациональный народ" Конституция РФ

 

Страница сгенерирована за 0.155 секунд. Запросов: 26.