Скрипт с циклом из файла

Скрипт с циклом из файла thumbnail

Bash-скрипты: начало

Bash-скрипты, часть 2: циклы

Bash-скрипты, часть 3: параметры и ключи командной строки

Bash-скрипты, часть 4: ввод и вывод

Bash-скрипты, часть 5: сигналы, фоновые задачи, управление сценариями

Bash-скрипты, часть 6: функции и разработка библиотек

Bash-скрипты, часть 7: sed и обработка текстов

Bash-скрипты, часть 8: язык обработки данных awk

Bash-скрипты, часть 9: регулярные выражения

Bash-скрипты, часть 10: практические примеры

Bash-скрипты, часть 11: expect и автоматизация интерактивных утилит

В прошлый раз мы рассказали об основах программирования для bash. Даже то немногое, что уже разобрано, позволяет всем желающим приступить к автоматизации работы в Linux. В этом материале продолжим рассказ о bash-скриптах, поговорим об управляющих конструкциях, которые позволяют выполнять повторяющиеся действия. Речь идёт о циклах for и while, о методах работы с ними и о практических примерах их применения.

Внимание: в посте спрятана выгода!

Циклы for

Оболочка bash поддерживает циклы for, которые позволяют организовывать перебор последовательностей значений. Вот какова базовая структура таких циклов:

for var in list do команды done

В каждой итерации цикла в переменную var будет записываться следующее значение из списка list. В первом проходе цикла, таким образом, будет задействовано первое значение из списка. Во втором – второе, и так далее – до тех пор, пока цикл не дойдёт до последнего элемента.

Перебор простых значений

Пожалуй, самый простой пример цикла for в bash-скриптах – это перебор списка простых значений:

#!/bin/bash for var in first second third fourth fifth do echo The $var item done

Ниже показаны результаты работы этого скрипта. Хорошо видно, что в переменную $var последовательно попадают элементы из списка. Происходит так до тех пор, пока цикл не дойдёт до последнего из них.

Простой цикл for

Обратите внимание на то, что переменная $var сохраняет значение при выходе из цикла, её содержимое можно менять, в целом, работать с ней можно как с любой другой переменной.

Перебор сложных значений

В списке, использованном при инициализации цикла for, могут содержаться не только простые строки, состоящие из одного слова, но и целые фразы, в которые входят несколько слов и знаков препинания. Например, всё это может выглядеть так:

#!/bin/bash for var in first “the second” “the third” “I’ll do it” do echo “This is: $var” done

Вот что получится после того, как этот цикл пройдётся по списку. Как видите, результат вполне ожидаем.

Перебор сложных значений

TNW-CUS-FMP – промо-код на 10% скидку на наши услуги, доступен для активации в течение 7 дней”

Инициализация цикла списком, полученным из результатов работы команды

Ещё один способ инициализации цикла for заключается в передаче ему списка, который является результатом работы некоей команды. Тут используется подстановка команд для их исполнения и получения результатов их работы.

#!/bin/bash file=”myfile” for var in $(cat $file) do echo ” $var” done

В этом примере задействована команда cat, которая читает содержимое файла. Полученный список значений передаётся в цикл и выводится на экран. Обратите внимание на то, что в файле, к которому мы обращаемся, содержится список слов, разделённых знаками перевода строки, пробелы при этом не используются.

Цикл, который перебирает содержимое файла

Тут надо учесть, что подобный подход, если ожидается построчная обработка данных, не сработает для файла более сложной структуры, в строках которого может содержаться по несколько слов, разделённых пробелами. Цикл будет обрабатывать отдельные слова, а не строки.

Что, если это совсем не то, что нужно?

Разделители полей

Причина вышеописанной особенности заключается в специальной переменной окружения, которая называется IFS (Internal Field Separator) и позволяет указывать разделители полей. По умолчанию оболочка bash считает разделителями полей следующие символы:

  • Пробел
  • Знак табуляции
  • Знак перевода строки

Если bash встречает в данных любой из этих символов, он считает, что перед ним – следующее самостоятельное значение списка.

Для того, чтобы решить проблему, можно временно изменить переменную среды IFS. Вот как это сделать в bash-скрипте, если исходить из предположения, что в качестве разделителя полей нужен только перевод строки:

IFS=$’n’

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

#!/bin/bash file=”/etc/passwd” IFS=$’n’ for var in $(cat $file) do echo ” $var” done

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

Построчный обход содержимого файла в цикле for

Разделителями могут быть и другие символы. Например, выше мы выводили на экран содержимое файла /etc/passwd. Данные о пользователях в строках разделены с помощью двоеточий. Если в цикле нужно обрабатывать подобные строки, IFS можно настроить так:

IFS=:

Обход файлов, содержащихся в директории

Один из самых распространённых вариантов использования циклов for в bash-скриптах заключается в обходе файлов, находящихся в некоей директории, и в обработке этих файлов.

Например, вот как можно вывести список файлов и папок:

#!/bin/bash for file in /home/likegeeks/* do if [ -d “$file” ] then echo “$file is a directory” elif [ -f “$file” ] then echo “$file is a file” fi done

Если вы разобрались с предыдущим материалом из этой серии статей, вам должно быть понятно устройство конструкции if-then, а так же то, как отличить файл от папки. Если вам сложно понять вышеприведённый код, перечитайте этот материал.

Вот что выведет скрипт.

Вывод содержимого папки

Обратите внимание на то, как мы инициализируем цикл, а именно – на подстановочный знак «*» в конце адреса папки. Этот символ можно воспринимать как шаблон, означающий: «все файлы с любыми именами». он позволяет организовать автоматическую подстановку имён файлов, которые соответствуют шаблону.

При проверке условия в операторе if, мы заключаем имя переменной в кавычки. Сделано это потому что имя файла или папки может содержать пробелы.

Циклы for в стиле C

Если вы знакомы с языком программирования C, синтаксис описания bash-циклов for может показаться вам странным, так как привыкли вы, очевидно, к такому описанию циклов:

Читайте также:  Мой календарь длина цикла

for (i = 0; i < 10; i++) { f(“number is %dn”, i); }

В bash-скриптах можно использовать циклы for, описание которых выглядит очень похожим на циклы в стиле C, правда, без некоторых отличий тут не обошлось. Схема цикла при подобном подходе выглядит так:

for (( начальное значение переменной ; условие окончания цикла; изменение переменной ))

На bash это можно написать так:

for (( a = 1; a < 10; a++ ))

А вот рабочий пример:

#!/bin/bash for (( i=1; i <= 10; i++ )) do echo “number is $i” done

Этот код выведет список чисел от 1 до 10.

Работа цикла в стиле C

Цикл while

Конструкция for – не единственный способ организации циклов в bash-скриптах. Здесь можно пользоваться и циклами while. В таком цикле можно задать команду проверки некоего условия и выполнять тело цикла до тех пор, пока проверяемое условие возвращает ноль, или сигнал успешного завершения некоей операции. Когда условие цикла вернёт ненулевое значение, что означает ошибку, цикл остановится.

Вот схема организации циклов while

while команда проверки условия

do

другие команды

done

Взглянем на пример скрипта с таким циклом:

#!/bin/bash var1=5 while [ $var1 -gt 0 ] do echo $var1 var1=$[ $var1 – 1 ] done

На входе в цикл проверяется, больше ли нуля переменная $var1. Если это так, выполняется тело цикла, в котором из значения переменной вычитается единица. Так происходит в каждой итерации, при этом мы выводим в консоль значение переменной до его модификации. Как только $var1 примет значение 0, цикл прекращается.

Результат работы цикла while

Если не модифицировать переменную $var1, это приведёт к попаданию скрипта в бесконечный цикл.

Вложенные циклы

В теле цикла можно использовать любые команды, в том числе – запускать другие циклы. Такие конструкции называют вложенными циклами:

#!/bin/bash for (( a = 1; a <= 3; a++ )) do echo “Start $a:” for (( b = 1; b <= 3; b++ )) do echo ” Inner loop: $b” done done

Ниже показано то, что выведет этот скрипт. Как видно, сначала выполняется первая итерация внешнего цикла, потом – три итерации внутреннего, после его завершения снова в дело вступает внешний цикл, потом опять – внутренний.

Вложенные циклы

Обработка содержимого файла

Чаще всего вложенные циклы используют для обработки файлов. Так, внешний цикл занимается перебором строк файла, а внутренний уже работает с каждой строкой. Вот, например, как выглядит обработка файла /etc/passwd:

#!/bin/bash IFS=$’n’ for entry in $(cat /etc/passwd) do echo “Values in $entry -” IFS=: for value in $entry do echo ” $value” done done

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

Обработка данных файла

Такой подход можно использовать при обработке файлов формата CSV, или любых подобных файлов, записывая, по мере надобности, в переменную окружения IFS символ-разделитель.

Управление циклами

Возможно, после входа в цикл, нужно будет остановить его при достижении переменной цикла определённого значения, которое не соответствует изначально заданному условию окончания цикла. Надо ли будет в такой ситуации дожидаться нормального завершения цикла? Нет конечно, и в подобных случаях пригодятся следующие две команды:

  • break
  • continue

Команда break

Эта команда позволяет прервать выполнение цикла. Её можно использовать и для циклов for, и для циклов while:

#!/bin/bash for var1 in 1 2 3 4 5 6 7 8 9 10 do if [ $var1 -eq 5 ] then break fi echo “Number: $var1” done

Такой цикл, в обычных условиях, пройдётся по всему списку значений из списка. Однако, в нашем случае, его выполнение будет прервано, когда переменная $var1 будет равна 5.

Досрочный выход из цикла for

Вот – то же самое, но уже для цикла while:

#!/bin/bash var1=1 while [ $var1 -lt 10 ] do if [ $var1 -eq 5 ] then break fi echo “Iteration: $var1” var1=$(( $var1 + 1 )) done

Команда break, исполненная, когда значение $var1 станет равно 5, прерывает цикл. В консоль выведется то же самое, что и в предыдущем примере.

Команда continue

Когда в теле цикла встречается эта команда, текущая итерация завершается досрочно и начинается следующая, при этом выхода из цикла не происходит. Посмотрим на команду continue в цикле for:

#!/bin/bash for (( var1 = 1; var1 < 15; var1++ )) do if [ $var1 -gt 5 ] && [ $var1 -lt 10 ] then continue fi echo “Iteration number: $var1” done

Когда условие внутри цикла выполняется, то есть, когда $var1 больше 5 и меньше 10, оболочка исполняет команду continue. Это приводит к пропуску оставшихся в теле цикла команд и переходу к следующей итерации.

Команда continue в цикле for

Обработка вывода, выполняемого в цикле

Данные, выводимые в цикле, можно обработать, либо перенаправив вывод, либо передав их в конвейер. Делается это с помощью добавления команд обработки вывода после инструкции done.

Например, вместо того, чтобы показывать на экране то, что выводится в цикле, можно записать всё это в файл или передать ещё куда-нибудь:

#!/bin/bash for (( a = 1; a < 10; a++ )) do echo “Number is $a” done > myfile.txt echo “finished.”

Оболочка создаст файл myfile.txt и перенаправит в этот файл вывод конструкции for. Откроем файл и удостоверимся в том, что он содержит именно то, что ожидается.

Перенаправление вывода цикла в файл

Пример: поиск исполняемых файлов

Давайте воспользуемся тем, что мы уже разобрали, и напишем что-нибудь полезное. Например, если надо выяснить, какие именно исполняемые файлы доступны в системе, можно просканировать все папки, записанные в переменную окружения PATH. Весь арсенал средств, который для этого нужен, у нас уже есть, надо лишь собрать всё это воедино:

#!/bin/bash IFS=: for folder in $PATH do echo “$folder:” for file in $folder/* do if [ -x $file ] then echo ” $file” fi done done

Такой вот скрипт, небольшой и несложный, позволил получить список исполняемых файлов, хранящихся в папках из PATH.

Читайте также:  Блок схема цикл с постусловием пример

Поиск исполняемых файлов в папках из переменной PATH

Итоги

Сегодня мы поговорили о циклах for и while в bash-скриптах, о том, как их запускать, как ими управлять. Теперь вы умеете обрабатывать в циклах строки с разными разделителями, знаете, как перенаправлять данные, выведенные в циклах, в файлы, как просматривать и анализировать содержимое директорий.

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

Уважаемые читатели! В комментариях к предыдущему материалу вы рассказали нам много интересного. Уверены, всё это окажет неоценимую помощь тем, кто хочет научиться программировать для bash. Но тема эта огромна, поэтому снова просим знатоков поделиться опытом, а новичков – впечатлениями.

Источник

как перебирать каждую строку текстового файла с помощью Баш?

этот скрипт:

echo “Start!” for p in (peptides.txt) do echo “${p}” done

Я получаю этот вывод на экране:

Start! ./runPep.sh: line 3: syntax error near unexpected token `(‘ ./runPep.sh: line 3: `for p in (peptides.txt)’

(позже я хочу сделать что-то более сложное с $p чем просто вывод на экран.)

переменной окружения SHELL is (от env):

SHELL=/bin/bash

/bin/bash –version выход:

GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu) Copyright (C) 2005 Free Software Foundation, Inc.

cat /proc/version вывод:

Linux version 2.6.18.2-34-default (geeko@buildhost) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006

файл пептидов.txt содержит:

RKEKNVQ IPKKLLQK QYFHQLEKMNVK IPKKLLQK GDLSTALEVAIDCYEK QYFHQLEKMNVKIPENIYR RKEKNVQ VLAKHGKLQDAIN ILGFMK LEDVALQILL

11 ответов

один из способов сделать это:

while read p; do echo “$p” done <peptides.txt

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

while IFS=”” read -r p || [ -n “$p” ] do f ‘%sn’ “$p” done < peptides.txt

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

while read -u 10 p; do … done 10<peptides.txt

вот, 10 это просто произвольное число (отличное от 0, 1, 2).

1571

автор: Bruno De Fraine

cat peptides.txt | while read line do # do something with $line here done

вариант 1a: while loop: одна строка за раз: перенаправление ввода

#!/bin/bash filename=’peptides.txt’ echo Start while read p; do echo $p done < $filename

вариант 1b: в то время как цикл: одна строка за раз:

Откройте файл, прочитайте из дескриптора файла (в данном случае файловый дескриптор #4).

#!/bin/bash filename=’peptides.txt’ exec 4<$filename echo Start while read -u4 p ; do echo $p done

Вариант 2: for loop: чтение файла в одну переменную и синтаксический анализ.

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

#!/bin/bash filename=’peptides.txt’ filelines=`cat $filename` echo Start for line in $filelines ; do echo $line done

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

for word in $(cat peptides.txt); do echo $word; done

этот формат позволяет мне поместить все это в одну командную строку. Измените часть “echo $word” на то, что вы хотите, и вы можете выдавать несколько команд, разделенных точкой с запятой. В следующем примере используется файл содержимое в качестве аргументов в двух других сценариях, которые вы, возможно, написали.

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done

или если вы собираетесь использовать это как редактор потока (learn sed), вы можете сбросить вывод в другой файл следующим образом.

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done > outfile.txt

я использовал их, как написано выше, потому что я использовал текстовые файлы, где я создал их с одним словом в строке. (См. комментарии) Если у вас есть пробелы, которые вы не хотите разбивать свои слова / строки, это становится немного уродливее, но та же команда все еще работает как следует:

OLDIFS=$IFS; IFS=$’n’; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS

Это просто говорит оболочке разбиваться только на новые строки, а не пробелы, а затем возвращает среду обратно к тому, что было ранее. На этом этапе вы можете рассмотреть вопрос о том, чтобы поместить все это в сценарий оболочки, а не сжимать все это в одну строку.

удачи!

еще несколько вещей, не охваченных другими ответами:

# ‘:’ is the delimiter here, and there are three fields on each line in the file # IFS set below is restricted to the context of `read`, it doesn’t affect any other code while IFS=: read -r field1 field2 field3; do # process the fields # if the line has less than three fields, the missing fields will be set to an empty string # if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s) done < input.txt while read -r line; do # process the line done < <(command …)

этот подход лучше, чем command … | while read -r line; do … потому что цикл while здесь работает в текущей оболочке, а не в подсетке, как в случае последней. См. соответствующий пост переменная изменяется внутри цикла while не вспомнил.

Читайте также:  Орнитиновый цикл все подробно

while read -r -d ” line; do # logic # use a second ‘read … <<< “$line”‘ if we need to tokenize the line done < <(find /path/to/dir -0)

обзоры читать: BashFAQ / 020 – как я могу найти и безопасно обрабатывать имена файлов, содержащие новые строки, пробелы или оба?

while read -u 3 -r line1 && read -u 4 -r line2; do # process the lines # note that the loop will end when we reach EOF on either of the files, because of the `&&` done 3< input1.txt 4< input2.txt

на основе @chepner это ответ здесь:

-u является расширением bash. Для совместимости с POSIX, каждый колл будет выглядеть как read -r X <&3.

while read -r line; do my_array+=(“$line”) done < my_file

если файл заканчивается неполной строкой (новая строка отсутствует в конце), то:

while read -r line || [[ $line ]]; do my_array+=(“$line”) done < my_file readarray -t my_array < my_file

или

mapfile -t my_array < my_file

а то

for line in “${my_array[@]}”; do # process the lines done

  • подробнее о оболочке builtins read и readarray команды-GNU

  • подробнее о IFS Википедии

  • BashFAQ/001 – как я могу читать файл (поток данных, переменная) строка за строкой (и / или поле за полем)?

по теме:

  • Создание массива из текстового файла в Bash
  • в чем разница между подходами к чтению файла, который имеет всего одну строчку?

Если вы не хотите, чтобы ваше чтение было нарушено символом новой строки, используйте –

#!/bin/bash while IFS=” read -r line || [[ -n “$line” ]]; do echo “$line” done < “”

затем запустите скрипт с именем файла в качестве параметра.

Предположим, у вас есть этот файл:

$ cat /tmp/test.txt Line 1 Line 2 has leading space Line 3 followed by blank line Line 5 (follows a blank line) and has trailing space Line 6 has no ending CR

есть четыре элемента, которые изменят значение выходного файла, считываемого многими решениями Bash:

  1. пустая строка 4;
  2. начальные или конечные пробелы на две линии;
  3. сохранение значения отдельных строк (т. е. каждая строка является записью);
  4. строка 6 не заканчивается CR.

если вы хотите текстовый файл строка за строкой включая пустые строки и завершающие строки без CR, вы должны использовать цикл while, и у вас должен быть альтернативный тест для последней строки.

вот методы, которые могут изменить файл (по сравнению с тем, что cat возвращает):

1) потерять последнюю строку и ведущие и конечные пробелы:

$ while read -r p; do f “%sn” “‘$p'”; done </tmp/test.txt ‘Line 1’ ‘Line 2 has leading space’ ‘Line 3 followed by blank line’ ” ‘Line 5 (follows a blank line) and has trailing space’

(если у вас while IFS= read -r p; do f “%sn” “‘$p'”; done </tmp/test.txt вместо этого вы сохраняете начальные и конечные пробелы, но все равно теряете последнюю строку, если она не заканчивается CR)

2) Использование замены процесса на cat Уилл читает весь файл одним глотком и теряет смысл отдельных строк:

$ for p in “$(cat /tmp/test.txt)”; do f “%sn” “‘$p'”; done ‘Line 1 Line 2 has leading space Line 3 followed by blank line Line 5 (follows a blank line) and has trailing space Line 6 has no ending CR’

(если убрать ” С $(cat /tmp/test.txt) Вы читаете файл, слово в слово, а не залпом. Также, вероятно, не то, что задумано…)

самый надежный и простой способ прочитать файл по строкам и сохранить все интервалы:

$ while IFS= read -r line || [[ -n $line ]]; do f “‘%s’n” “$line”; done </tmp/test.txt ‘Line 1’ ‘ Line 2 has leading space’ ‘Line 3 followed by blank line’ ” ‘Line 5 (follows a blank line) and has trailing space ‘ ‘Line 6 has no ending CR’

если вы хотите штрипса и торговые пространства, удалить IFS= детали:

$ while read -r line || [[ -n $line ]]; do f “‘%s’n” “$line”; done </tmp/test.txt ‘Line 1’ ‘Line 2 has leading space’ ‘Line 3 followed by blank line’ ” ‘Line 5 (follows a blank line) and has trailing space’ ‘Line 6 has no ending CR’

(текстовый файл без расторжения n, хотя и довольно распространен, считается сломанным под POSIX. Если вы можете рассчитывать на трейлинг -n не нужно || [[ -n $line ]] на while петли.)

больше на BASH FAQ

#!/bin/bash # # Change the file name from “test” to desired input file # (The s in bash are prefixed with #’s) for x in $(cat test.txt) do echo $x done

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

##Parse FPS from first video stream, drop quotes from fps variable ## streams.stream.0.codec_type=”video” ## streams.stream.0.r_frame_rate=”24000/1001″ ## streams.stream.0.avg_frame_rate=”24000/1001″ FPS=unknown while read -r line; do if [[ $FPS == “unknown” ]] && [[ $line == *”.codec_type=”video””* ]]; then echo ParseFPS $line FPS=parse fi if [[ $FPS == “parse” ]] && [[ $line == *”.r_frame_rate=”* ]]; then echo ParseFPS $line FPS=${line##*=} FPS=”${FPS%”}” FPS=”${FPS#”}” fi done <<< “$(ffprobe -v quiet -_format flat -show_format -show_streams -i “$input”)” if [ “$FPS” == “unknown” ] || [ “$FPS” == “parse” ]; then echo ParseFPS Unknown frame rate fi echo Found $FPS

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

Loop match для подстрок затем читает name=value пара, разбивает правую часть последнего = характер, падает первая цитата, падает последняя цитата, Мы имеем чистое значение, который нужно использовать в другом месте.

@Peter: это может сработать для вас –

echo “Start!”;for p in $(cat ./pep); do echo $p done

это будет выходной-

Start! RKEKNVQ IPKKLLQK QYFHQLEKMNVK IPKKLLQK GDLSTALEVAIDCYEK QYFHQLEKMNVKIPENIYR RKEKNVQ VLAKHGKLQDAIN ILGFMK LEDVALQILL

Источник