В этой статье мы продолжим наш цикл про написание скриптов на Bash. Мы уже рассмотрели как работать с архивами и создавать функции, но этого еще недостаточно. Любой уважающий себя язык программирования должен содержать циклы. Цикл - это такая последовательность, которая позволяет выполнять определенный участок кода необходимое количество раз.
С помощью циклов вы можете очень сильно сократить количество строк кода, которые необходимо написать для однотипных операций. В этой статье мы рассмотрим что такое циклы Bash, как их создавать и использовать.
Содержание статьи
Циклы Bash
Как я уже сказал, циклы позволяют выполнять один и тот же участок кода необходимое количество раз. В большинстве языков программирования существует несколько типов циклов. Большинство из них поддерживаются оболочкой Bash. Мы рассмотрим их все в сегодняшней статье, но сначала поговорим какими они бывают:
- for - позволяет перебрать все элементы из массива или использует переменную-счетчик для определения количества повторений;
- while - цикл выполняется пока условие истинно;
- until - цикл выполняется пока условие ложно.
Циклы Bash, это очень полезная вещь и разобраться с ними будет несложно. Bash позволяет использовать циклы как в скриптах, так и непосредственно в командной оболочке. Дальше мы рассмотрим каждый из этих видов циклов.
Цикл for
Цикл for bash применяется очень часто из-за своей специфики. Его проще всего использовать когда вы знаете сколько раз нужно повторить операцию или вам нужно просто обработать по очереди все элементы массива и вы не хотите контролировать количество повторений.
Цикл for имеет несколько синтаксисов и может вести себя по разному. Для перебора элементов списка удобно использовать такой синтаксис:
for переменная in список
do
команда1
команда2
done
Каждый цикл for независимо от типа начинается с ключевого слова for. Дальше все зависит от типа. В этом случае после for указывается имя переменной, в которую будет сохранен каждый элемент списка, затем идет ключевое слово in и сам список. Команды, которые нужно выполнять в цикле размещаются между словами do и done.
Проверим все на практике и напишем небольшой скрипт, который будет выводить номера от 1 до 5:
vi for1.sh
!/bin/bash
for number in 1 2 3 4 5
do
echo $number
done

Дайте скрипту права на выполнение и запустите его:
chmod +x for1.sh
./for1.sh
Вы увидите, что все выполняется так, как и предполагалось. Программа выведет цифры от 1 до 5, которые мы перечислили в массиве. Вы можете передать циклу любой массив, например, вывод какой-либо другой команды:
!/bin/bash
for iface in $(ls /sys/class/net/)
do
echo $iface
done
Как вы уже поняли, этот цикл выводит список всех, подключенных к системе сетевых интерфейсов. Но в цикле вы можете не только их выводить, но и выполнять какие-либо действия.
Следующий тип цикла for похож на его реализацию в языках программирования Си и С++. Он состоит из трех частей, инициализатора счетчика, условия продолжения выполнения и действия над счетчиком. Вот синтаксис:
for ((счетчик=1; счетчик < 10; счетчик++))
do
команда1
команда2
done
Этот цикл немного сложнее, но он позволяет сделать больше. С помощью такого цикла можно не только перебирать массивы, но и сделать необходимое количество повторений. Рассмотрим пример:
!/bin/bash
for ((i=1; i < 10; i++))
do
echo $i
done
Результат странный, правда? Обратите внимание, как выполняется проверка условия. Значение счетчика сравнивается с эталоном. Действие с переменной счетчиком выполняется сразу же после завершения основного блока команд, а уже потом делается сравнение. Таким образом, у нас при первом выполнении i равно 1, а после него уже два 2. Теперь к завершению девятого прохода значение счетчика будет увеличено на единицу и станет 10. Условие сравнения 10 < 10 не верно, поэтому цикл дальше не выполняется.
С помощью этого же синтаксиса можно сделать бесконечные циклы bash linux:
!/bin/bash
for ((;;))
do
echo "Бесконечный цикл, нажмите CTRL+C для выхода"
done
Если необходимо, вы можете выйти из цикла преждевременно. Но так делать не рекомендуется. Для выхода используйте команду break:
!/bin/bash
for (i=1;i<10;i++)
do
echo Значение счетчика $i
if [[ i -gt 5]]
break
fi
done
Со циклами for мы завершили, теперь вы знаете как они выглядят и что с ними делать. Дальше мы рассмотрим циклы while и until. Они намного проще и более универсальны.
Цикл While
Суть цикла While в том, что он будет повторяться до тех пор, пока будет выполняться условие, указанное в объявлении цикла. Здесь нет никаких счетчиков и если они вам нужны, то организовывать их вам придется самим. Bash цикл while имеет такой синтаксис:
while [ условие ]
do
команда1
команда2
команда3
done
Это работает так: сначала выполняется проверка на правильность условия, если true, выполняется набор команд, затем снова выполняется проверка, и так пока условие не вернет отрицательный результат. Это нам тоже нужно сделать вручную. Рассмотрим пример:
vi while.sh
!/bin/bash
x=1
while [ $x -lt 5 ]
do
echo "Значение счетчика: $x"
x=$(( $x + 1 ))
done
Здесь сначала мы устанавливаем значение счетчика в единицу, затем, в условии сравниваем его с 5, пока счетчик меньше пяти будут выполняться команды из блока do-done. Но еще в этом блоке нам нужно увеличивать значение счетчика на единицу, что мы и делаем.
Также с помощью while мы можем прочитать несколько строк из стандартного ввода:
vi while.sh
!/bin/bash
hile read line
do
echo $line
done
Программа будет запрашивать новые строки пока вы не передадите ей символ конца файла с помощью сочетания клавиш Ctrl+D. Бесконечный цикл while bash с помощью while еще проще организовать:
vi while.sh
Цикл until
Нам осталось рассмотреть последний цикл. Это цикл until. Он очень похож на while и отличается от него только работой условия. Если в первом нужно чтобы условие всегда было истинным, то здесь все наоборот. Цикл будет выполняться пока условие неверно. Синтаксис:
until [ условие ]
do
команда1
команда2
done
Думаю после рассмотрения реального примера со счетчиком будет намного понятнее как это работает:
!/bin/bash
count=1
until [ $count -gt 10 ]
do
echo "Значение счетчика: $count"
count=$(( $count + 1 ))
done
Мы задаем значение счетчика 1 и увеличиваем его на единицу при каждом проходе. Как только условие счетчик больше 10 выполнится, сразу цикл будет остановлен. В циклах while и until тоже можно использовать команды break и continue для выхода из цикла и завершения текущего прохода, но мы не будем это очень подробно рассматривать.
Выводы
В этой статье мы рассмотрели циклы Bash, как с ними работать и в каких ситуациях их можно использовать. Циклы могут стать отличным и незаменимым инструментом при создании сложных скриптов администрирования системы. Если у вас остались вопросы, спрашивайте в комментариях!
Anubis – это максимально легкое open-source решение, созданное специально для защиты небольших веб-ресурсов от бесконечного потока запросов от ботов и AI парсеров. Этот инструмент можно считать "ядерным вариантом", потому что он заставляет ботов выполнять вычисления похожие на майнинг криптовалюты. Но это неплохая альтернатива для тех, кто не может или не хочет использовать Cloudflare. Посмотреть детали
















В первом примере переменная записана с опечаткой - number/numner, хорошо бы поправить
Да тут всем похер.
3 (три) года прошло, а ошибка все еще на месте
Комменты никто не читает
з.ы. ребята с гигбрейнс тут?
а если надо обработать текстовый файл , как удалить не нужные строки
cat ./'текстовый файл' | sed '/^ненужная строка$/d'
Если ненужных строк много - то можно создать список не нужных строк и крутануть его через for, но, если есть пробельные символы (пробел или табуляция) в строках, то есть нюансы:
1. в sed подставльть можно так: sed '/^'"$string"'/d'
2. будет трабла в выводе for, но об этом напишу в коментарии ниже, как добавление к этой замечательной статье
Если в списке ненужных строк будет встречаться символ косой черты / - будет трабла с sed, как вариант рещения - подготовка строки для sed: echo "$string" | sed 's/\//\\\//g' или echo "$string" | sed 's!/!\\/!g'
Что-то типа:
#!/bin/bash
del_table # сюда пишем ненужные строки, например считаем файл: del_table=$(cat ./file_string)
my_text # сюда пишем свой исходный текст, например считаем файл: del_table=$(cat ./file_text)
IFS=$'\n'
for string in $del_table
do
string=$(echo "$string" | sed 's/\//\\\//g')
my_text=$(echo "$my_text" | sed '/^'"$string"'$/d')
done
echo "$my_text"
exit 0
Наткнулся на такую ситуацию, не понял в чём проблема, а по поиску сильно в тему особо не находилось. Если переменная изменяется в цикле, получающем что-то из конвеера, после выхода цикла её значение останется, каким было до цикла. В итоге где-то подсказали, что в таком случае цикл запускается типа в дочернем потоке из которого нельзя вернуть значения переменных. Нужно обойтись без конвеера.
Проблема:
cd \
kkk=0
ls -1 | while read sss
do
echo -n "$sss "
let "kkk+=1"
echo -e "\t$kkk"
done
echo -e "\n$kkk"
# $kkk после цикла таки - 0
Ожидаемая работа:
cd /
kkk=0
while read sss
do
echo -n "$sss"
let "kkk+=1"
echo -e "\t$kkk"
done << EOF
$(ls -1)
EOF
echo -e "\n$kkk"
Да и цикл у Вас работает в другом подпроцессе, и read...но вся трабла в read - уберите его и будет Вам фэншуй 😉 а цикл сразу же вернёт то, что надо.
Варианты:
1. Вариант с циклом без read ():
cd /
kkk=0
IFS=$'\n'
for sss in `ls -1`
do echo -e "$sss\t$(( kkk++ ))"
done
2. Вариант без цикла с cat (самый быстрый в работе):
cd /
ls -1 | cat -n
Чтоб точно соответствовало вашему выводу:
ls -1 ~ | cat -n | sed 's/^[ \t]*\([0-9][0-9]*\)[ \t]*\(.*\)/\2\t\1/'
Небольшой довесок к статье: циклом for можно управлять, т.е. если мы на вход пододим несколько строк с пробелами, то for сделает нам каку, т.к. по умолчанию, как разделитель, используются и пробельные символы, и символы окончания строки.
1. В некоторых случаях это просто обходится, например, подаём нашему скрипту аргументы с пробелами, здесь нет символа окончания строки, поэтому всё просто:
for arg in "$@"
do echo "\"$arg\""
done
Если подали (назовём скрипт test), например: ./test 'привет losst' ':)', то получим две строки:
"привет losst"
":)"
А если бы было без кавычек - for arg in $@, то получили бы двух входящих, три выходящие строки:
"привет"
"losst"
":)"
2. Если мы будем использовать ещё и символы окончания строки, например, отрывок вывода ls -1 ~:
Общедоступные
Рабочий стол
то for снова "некорректно" отработает и при наборе for string in `ls -1 ~`; do echo "\"$string\""; done, выдаст:
"Общедоступные"
"Рабочий"
"стол"
А при убирании в кавычки - for string in "`ls -1 ~`"; do echo "\"$string\""; done, вообще одну строку:
"Общедоступные
Рабочий стол"
Решение: есть переменная окружения IFS, которая управляет разделитялями в BASH, т.е. исходя из примера выше, просто прикажем циклу for использовать в качестве разделителя только символ окончания строки - IFS=$'\n'; for string in `ls -1 ~`; do echo "\"$string\""; done и теперь, неиспользуя кавычки, мы получаем заведома нужное:
"Общедоступные"
"Рабочий стол"
Если вдруг будете писать это не на bash, а на голом sh, то получиться, что IFS=''
Спасибо за полезный комментарий. Пригодилось, когда надо было выводить строку, которая содержит в качестве разделителя табуляцию между полями.
Удалить ненужные строки:
ls /sys/class/net/|egrep -vx "$(echo -e "^eno\n^lo"|paste -sd "|")"
Вместо ls /sys/class/net/ -- пишешь cat "исходный файл"
Вместо echo -e "^eno\n^lo" -- cat "файл с ненужными строками"
Если "файл с фрагментами ненужных строк" (то есть, надо удалить строки, СОДЕРЖАЩИЕ подстроки, а не полностью совпадающие), то вместо egrep -vx надо писать egrep -v
вы бы насоветовали эти каракули автору сайта, быть может это увеличило посещаемость))
Проверяйте примеры, ********.
!/bin/bash
for ((i=1; i < 10; i++))
do
echo $i
done
привет всем!
я ещё совсем маленький олень..
помогите чем могите, не догоняю как вставить дополнительное условие: 26 d_(){
if [ "$1" ];
cd "$1" && clear && echo " ${PWD##*/}/" && ls -l
then [ "$2" ];
cd "$2" && clear && echo " ${PWD}/" && ls -l
else
clear && echo " ${PWD##*/}" && ls -l
fi
}
if ["$1"]; cd "$1" и вывод рабочей директории " ${PWD##*/}/"
как вставить ещё одно условие if?
26 d_(){
27 if [ "$1" ];
28 cd "$1" && clear && echo " ${PWD##*/}/" && ls -l
29 then [ "$2" ];
30 cd "$2" && clear && echo " ${PWD}/" && ls -l
31 else
32 clear && echo " ${PWD##*/}" && ls -l
33 fi
34 }
дурак написал прогу для сообщений.
тут можно было сделать детский мат - проверить строчки и вернуть их к первоначальному смыслу
4 примера для FOR
for i in 1 2 3 4 5 6 7 8 9 10
for i in {1..10}
for i in `seq 1 10`
for ((i=1;i<11;i++))