Bash прочитать файл построчно

Обновлено: 06.07.2024

Как мне в цикле перебрать каждую строку текстового файла с помощью Bash ?

Я использую следующий скрипт:

echo "Start!"

for p in (peptides.txt)

do

echo "$

"

done

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

Start!

./runPep.sh: line 3: синтаксическая ошибка – неожиданная лексема "('

./runPep.sh: line 3: "for p in (peptides.txt)'

Ответ 1

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

while read p; do

echo "$p"

done <peptides.txt

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

while IFS="" read -r p || [ -n "$p" ]

do

printf '%s\n' "$p"

done < peptides.txt

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

while read -u 10 p; do

.

done 10<peptides.txt

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

Ответ 2

Ответ 3

Еще один способ выполнить данную операцию :

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

Или, если вы собираетесь использовать это как редактор потока (используя 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

Этот пример указывает оболочке разделять символы по строкам.

Ответ 4

Еще несколько возможных решений:

Чтение из файла с разделителями

Чтение вывода другой команды с использованием подстановки процесса

Чтение из ввода с разделителями NULL, например , find . -print0

Чтение из более чем одного файла за раз

Чтение всего файла в массив (версии Bash до 4)

while read -r line; do

my_array+=("$line")

done < my_file

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

while read -r line || [[ $line ]]; do

my_array+=("$line")

done < my_file

Чтение всего файла в массив (версии Bash 4x и новее)

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Favorite

Добавить в избранное (1 оценок, среднее: 5,00 из 5)

Как создать псевдонимы Bash

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

В этой статье мы расскажем о том, как построчно читать файл в Bash.

Считывание файла построчным синтаксисом

Наиболее общий синтаксис для чтения файла построчно:

или эквивалентная однострочная версия:

Как это работает?

Чтение файла построчные примеры

Давайте посмотрим на следующий пример. Предположим, у нас есть файл с именем, distros.txt содержащий список некоторых наиболее популярных дистрибутивов Linux и менеджеры пакетов, разделенные запятой (,):

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

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

В следующем примере мы устанавливаем IFS для запятой (,) и передаем две переменные distro и pm команде чтения. Все от начала строки до первой запятой будет присвоено первой переменной ( distro), а остальная часть строки будет назначена второй переменной ( pm):

Альтернативные методы чтения файлов

Использование процесса замены

Подстановка процесса позволяет вам передавать вывод команды в виде имени файла:

Использование строки Here

Здесь строка является вариантом heredoc. Строка (cat input_file ) будет содержать новые строки:

Использование файлового дескриптора

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

При работе с файловыми дескрипторами используйте число от 4 до 9, чтобы избежать конфликта с внутренними файловыми дескрипторами оболочки.

Заключение

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

Если у вас есть какие-либо вопросы или отзывы, не стесняйтесь оставлять комментарии.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Окно терминала в компьютерной системе Linux.

Фатмавати Ахмад Дзэнури / Shutterstock

Файлы, текст и идиомы

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

Чтение строк из файла: однострочный

В Bash вы можете использовать while цикл в командной строке, чтобы прочитать каждую строку текста из файла и что-то с ней сделать. Наш текстовый файл называется «data.txt». Он содержит список месяцев в году.

Наш простой однострочный текст:


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

Конечно, этот однострочник не очень полезен. Linux уже предоставляет cat команда, которая делает именно это за нас. Мы создали сложный способ заменить трехбуквенную команду. Но он наглядно демонстрирует принципы чтения из файла.

В определенной степени это работает достаточно хорошо. Предположим, у нас есть еще один текстовый файл, содержащий названия месяцев. В этом файле escape-последовательность для символа новой строки добавлена ​​к каждой строке. Назовем его «data2.txt».

Давайте воспользуемся однострочником в нашем новом файле.


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

Чтение строк из файла с помощью скрипта

Вот наш сценарий. Он называется «script1.sh».

Мы устанавливаем переменную с именем Counter к нулю, то определяем нашу while петля.

Первый оператор в строке while: IFS='' . IFS обозначает внутренний разделитель полей. Он содержит значения, которые Bash использует для определения границ слов. По умолчанию команда чтения удаляет начальные и конечные пробелы. Если мы хотим читать строки из файла в точности такими, какие они есть, нам нужно установить IFS быть пустой строкой.

Мы могли бы установить это один раз вне цикла, так же, как мы устанавливаем значение Counter . Но с более сложными сценариями, особенно со многими определяемыми пользователем функциями в них, возможно, что IFS могут быть установлены в другие значения в другом месте сценария. Обеспечение того, чтобы IFS устанавливается в пустую строку каждый раз, когда while loop iterates гарантирует, что мы знаем, каким будет его поведение.

Мы собираемся прочитать строку текста в переменной с именем LinefromFile . Мы используем -r (считайте обратную косую черту как обычный символ), чтобы игнорировать обратную косую черту. С ними будут обращаться так же, как с любым другим персонажем, и они не получат никакого специального обращения.

Есть два условия, которые удовлетворяют while цикл и разрешить обработку текста телу цикла:

  • read -r LinefromFile : Когда строка текста успешно считана из файла, read команда отправляет сигнал успеха while , а while loop передает поток выполнения в тело цикла. Обратите внимание, что read команда должна увидеть символ новой строки в конце строки текста, чтобы считать ее прочитанной. Если файл не POSIX совместимый текстовый файл, последняя строка может не включать символ новой строки. Если read команда видит маркер конца файла (EOF) перед тем, как строка будет завершена новой строкой, она будет не рассматривайте это как успешное чтение. Если это произойдет, последняя строка текста не будет передана в тело цикла и не будет обработана.
  • [ -n "$" ] : Нам нужно проделать дополнительную работу для обработки файлов, несовместимых с POSIX. Это сравнение проверяет текст, читаемый из файла. Если оно не завершено символом новой строки, это сравнение все равно вернет успех для while петля. Это гарантирует, что любые фрагменты завершающей строки обрабатываются телом цикла.

В теле нашего цикла мы увеличиваем Counter переменная на единицу и используя echo для отправки вывода в окно терминала. Отображаются номер строки и текст каждой строки.

Скопируйте и вставьте сценарий в редактор и сохраните его под именем «script1.sh». Использовать chmod команда сделать его исполняемым.

chmod + x script1.sh в окне терминала

Давайте посмотрим, что делает наш скрипт с текстовым файлом data2.txt и содержащимися в нем обратными косыми чертами.

./script1.sh data2.txt в окне терминала

Каждый символ в строке отображается дословно. Обратные косые черты не интерпретируются как escape-символы. Они печатаются как обычные символы.

Передача строки функции

Мы все еще просто выводим текст на экран. В реальном сценарии программирования мы, вероятно, собираемся сделать что-то более интересное со строкой текста. В большинстве случаев хорошей практикой программирования является обработка дальнейшей обработки строки в другой функции.

Вот как мы могли это сделать. Это «script2.sh».

Мы определяем наши Counter как и раньше, а затем мы определяем функцию с именем process_line() . Должно появиться определение функции перед функция сначала вызывается в скрипте.

Наша функция будет передавать только что прочитанную строку текста на каждой итерации while петля. Мы можем получить доступ к этому значению в функции, используя $1 переменная. Если бы функции были переданы две переменные, мы могли бы получить доступ к этим значениям, используя $1 и $2 и так далее, чтобы узнать о других переменных.

Ш hile петля в основном такая же. В теле цикла есть только одно изменение. В echo линия заменена вызовом process_line() функция. Обратите внимание, что вам не нужно использовать скобки «()» в имени функции, когда вы ее вызываете.

Имя переменной, содержащей строку текста, LinefromFile , заключен в кавычки при передаче в функцию. Это касается строк, в которых есть пробелы. Без кавычек первое слово рассматривается как $1 по функции второе слово считается $2 , и так далее. Использование кавычек гарантирует, что вся строка текста будет обрабатываться как $1 . Обратите внимание, что это не тоже самое $1 который содержит тот же файл данных, переданный в сценарий.

Потому что Counter был объявлен в основной части скрипта, а не внутри функции, на него можно ссылаться внутри process_line() функция.

Скопируйте или введите приведенный выше сценарий в редактор и сохраните его под именем «script2.sh». Сделайте его исполняемым с помощью chmod :

chmod + x script2.sh в окне терминала

Теперь мы можем запустить его и передать новый файл данных «data3.txt». В нем есть список месяцев и одна строка со многими словами.

./script2.sh data3.txt в окне терминала

Строки считываются из файла и передаются одна за другой в process_line() функция. Все строки отображаются правильно, в том числе нечетная с обратным пробелом, кавычками и несколькими словами.

Строительные блоки полезны

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

Как мне перебрать каждую строку текстового файла с помощью Bash ?

С помощью этого скрипта:

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

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

Переменная окружения SHELL (из env):

/bin/bash --version вывод:

cat /proc/version вывод:

Файл peptides.txt содержит:

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

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

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

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

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

Как я должен интерпретировать последнюю строку? Файл peptides.txt перенаправляется на стандартный ввод и как-то на весь блок while? Msgstr "Вставьте peptides.txt в этот цикл while, чтобы команде read было что потреблять". Мой метод "кошка" похож, посылая вывод команды в блок while для потребления "read", только он запускает другую программу для выполнения работы. Этот метод, кажется, пропускает последнюю строку файла. Двойные кавычки строк! эхо "$ p" и файл .. поверьте мне, это укусит вас, если вы этого не сделаете . Я ЗНАЮ! LOL Обе версии не могут прочитать последнюю строку, если она не заканчивается новой строкой. Всегда используйте while read p || [[ -n $p ]]; do .

и однострочный вариант:

Эти параметры пропускают последнюю строку файла, если нет перевода строки в конце.

Вы можете избежать этого с помощью следующего:

В общем, если вы используете «cat» только с одним аргументом, вы делаете что-то не так (или неоптимально). Да, это не так эффективно, как у Бруно, потому что запускает другую программу без необходимости. Если эффективность имеет значение, сделайте это Бруно. Я помню свой путь, потому что вы можете использовать его с другими командами, где синтаксис «перенаправление из» не работает. Я использую "cat file |" как начало многих моих команд исключительно потому, что я часто создаю прототип с помощью "head file |" Это может быть не так эффективно, но гораздо более читабельно, чем другие ответы.

Вариант 1а: цикл «цикл»: по одной строке: перенаправление ввода

Вариант 1b: цикл «цикл»: по одной строке за раз:
открыть файл, прочитать из дескриптора файла (в данном случае дескриптор файла № 4).

Для варианта 1b: нужно ли снова закрывать файловый дескриптор? Например, петля может быть внутренней петлей. Дескриптор файла будет очищен при выходе из процесса. Явное закрытие может быть сделано для повторного использования числа fd. Чтобы закрыть fd, используйте другой exec с синтаксисом &, например: exec 4 <& - Спасибо за вариант 2. Я столкнулся с огромными проблемами с вариантом 1, потому что мне нужно было читать из stdin в цикле; в таком случае вариант 1 не будет работать. Вы должны более четко указать, что вариант 2 настоятельно не рекомендуется . @masgo Вариант 1b должен работать в этом случае и может быть объединен с синтаксисом перенаправления ввода из Варианта 1a путем замены done < $filename на done 4<$filename (что полезно, если вы хотите прочитать имя файла из параметра команды, и в этом случае вы можете просто заменить $filename на $1 ). Мне нужно перебрать содержимое файла, например tail -n +2 myfile.txt | grep 'somepattern' | cut -f3 , во время выполнения команд ssh внутри цикла (использует stdin); вариант 2 здесь представляется единственным выходом?

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

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

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

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

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

Bash $ (<peptides.txt), возможно, более элегантен, но все же неправильно, как правильно сказал Жоао, вы выполняете логику подстановки команд, где пробел или символ новой строки - это то же самое. Если в строке есть пробел, цикл выполняется ДВАЖДЫ или более для этой одной строки. Поэтому ваш код должен правильно читать: для слова в $ (<peptides.txt); делай . Если ты точно знаешь, что пробелов нет, то строка равна слову, и ты в порядке. @ JoaoCosta, maxpolk: Хорошие моменты, которые я не учел. Я отредактировал оригинальный пост, чтобы отразить их. Спасибо! Использование for делает входные токены / строки подчиненными расширениям оболочки, что обычно нежелательно; попробуйте это: for l in $(echo '* b c'); do echo "[$l]"; done - как вы увидите, * - хотя изначально он был заключен в кавычки - он расширяется до файлов в текущем каталоге. @dblanchard: последний пример, использующий $ IFS, должен игнорировать пробелы. Вы пробовали эту версию? То, как эта команда становится намного сложнее, когда решаются важные проблемы, очень хорошо показывает, почему использование for для перебора строк файла - плохая идея. Плюс аспект расширения, упомянутый @ mklement0 (даже если это возможно обойти, введя экранированные кавычки, что снова делает вещи более сложными и менее читаемыми).

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

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

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

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

Читайте также: