Linux

Цикл по содержимому файла в Bash

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

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

echo "Start!"

for p in (peptides.txt)

do

    echo "${p}"

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

Вариант 1: используя цикл while - по одной строке за раз: перенаправление ввода:

#!/bin/bash

filename='peptides.txt'

echo Start

while read p; do 

    echo $p

done < $filename

 

Вариант 2: цикл while - открыть файл, прочитать из файлового дескриптора (в данном случае файлового дескриптора №4).

#!/bin/bash

filename='peptides.txt'

exec 4<$filename

echo Start

while read -u4 p ; do

    echo $p

done

 

Ответ 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

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

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

# ':' является разделителем, и на каждой строке в файле имеются три поля

# IFS, установленный ниже, ограничен контекстом `read`, он не влияет на любой другой код

while IFS=: read -r field1 field2 field3; do

  # обработка полей

  # если строка содержит менее трех полей, то недостающие поля будут установлены в пустую строку

  # если строка имеет более трех полей, `field3` получит все значения, включая третье поле плюс разделитель(и)

done < input.txt

 

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

while read -r line; do

  # обработка строки

done < <(command ...)

 

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

 

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

while read -r -d '' line; do

  # использование второй строки 'read ... <<< "$line"', если нам нужно токенизировать строку

done < <(find /path/to/dir -print0)

 

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

while read -u 3 -r line1 && read -u 4 -r line2; do

  # обработка строк

  # обратите внимание, что цикл завершится, когда мы достигнем EOF в любом из файлов

done 3< input1.txt 4< input2.txt

 

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

 

Чтение всего файла в массив (версии 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 и новее)

readarray -t my_array < my_file

 

или же

mapfile -t my_array < my_file

 

А потом:

for line in "${my_array[@]}"; do

  # обработка строк

done 

Схожие статьи

Linux

Стоит ли блокировать ICMP?

Linux

Слишком длинный список аргументов: ошибка для команд rm, cp, mv

Linux

Как найти расположение MySQL my.cnf

Linux

Как мне запросить ввод Да/Нет/Отмена в сценарии оболочки Linux