Читайте файл построчно, присваивая значение переменной

593

У меня есть следующий файл .txt:

Marco
Paolo
Antonio

Я хочу читать это построчно, и для каждой строки я хочу назначить строковое значение .txt переменной. Предположим, моя переменная $name, поток:

  • Читать первую строку из файла
  • Назначьте $name= "Марко"
  • Выполните несколько задач с $name
  • Читать вторую строку из файла
  • Назначьте $name= "Паоло"
  • 1
    Возможный дубликат Looping через содержимое файла в Bash?
  • 3
    Можно ли как-то объединить эти вопросы? У обоих есть несколько действительно хороших ответов, которые подчеркивают различные аспекты проблемы, плохие ответы содержат подробные объяснения в комментариях, что в них плохого, и на данный момент вы не можете получить полный обзор того, что следует учитывать, из ответов один единственный вопрос от пары. Было бы полезно иметь все это в одном месте, а не на 2 страницы.
Теги:

10 ответов

1038
Лучший ответ

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

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "Text read from file: $line"
done < "$1"

Пояснение:

  • IFS='' (или IFS=) предотвращает обрезку ведущего/конечного пробела.
  • -r предотвращает интерпретацию экранов обратной косой черты.
  • || [[ -n $line ]] предотвращает игнорирование последней строки, если она не заканчивается на \n (поскольку read возвращает ненулевой код выхода, когда он встречает EOF).

Запустите script следующим образом:

chmod +x rr.sh
./rr.sh filename.txt

....

  • 18
    Есть предостережение с этим методом. Если что-то внутри цикла while является интерактивным (например, читает из stdin), то его вход будет принимать от $ 1. Вам не будет предоставлена возможность вводить данные вручную.
  • 6
    Следует отметить, что некоторые команды нарушают (как, например, они нарушают цикл) это. Например, ssh без флага -n фактически заставит вас выйти из цикла. Вероятно, для этого есть веская причина, но мне потребовалось некоторое время, чтобы понять, что приводило к сбою моего кода, прежде чем я это обнаружил.
Показать ещё 12 комментариев
290

Я рекомендую вам использовать флаг -r для read который обозначает:

-r  Do not treat a backslash character in any special way. Consider each
    backslash to be part of the input line.

Я цитирую man 1 read.

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

Вот обновленный код:

#!/usr/bin/bash
filename="$1"
while read -r line; do
    name="$line"
    echo "Name read from file - $name"
done < "$filename"
  • 3
    Обрезает начальные и конечные места от линии
  • 0
    @ Томас, а что происходит с пробелами посередине? Подсказка: нежелательная попытка выполнения команды.
Показать ещё 5 комментариев
121

Использование следующего шаблона Bash позволит вам читать по одному значению за раз из файла и обрабатывать его.

while read name; do
    # Do what you want to $name
done < filename
  • 13
    как однострочник: пока читаешь имя; сделать эхо $ {имя}; сделано <имя файла
  • 8
    За исключением того, что вы хотите read -r , и вам нужно заключить в кавычки "$name" .
Показать ещё 12 комментариев
51
#! /bin/bash
cat filename | while read LINE; do
    echo $LINE
done
  • 6
    Ничего не имею против других ответов, может быть, они более изощренные, но я одобряю этот ответ, потому что он прост, удобочитаем и достаточен для того, что мне нужно. Обратите внимание, что для того, чтобы он работал, текстовый файл, который нужно прочитать, должен заканчиваться пустой строкой (т. Е. Нужно нажимать Enter после последней строки), в противном случае последняя строка будет игнорироваться. По крайней мере, так случилось со мной.
  • 8
    Бесполезное использование кошки, не так ли?
Показать ещё 9 комментариев
19

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

#!/bin/bash
#
# This program reads lines from a file.
#

end_of_file=0
while [[ $end_of_file == 0 ]]; do
  read -r line
  # the last exit status is the 
  # flag of the end of file
  end_of_file=$?
  echo $line
done < "$1"
17

Использование:

filename=$1
IFS=$'\n'
for next in 'cat $filename'; do
    echo "$next read from $filename" 
done
exit 0

Если вы установили IFS другому, вы получите странные результаты.

  • 30
    Это ужасный метод . Пожалуйста, не используйте его, если вы не хотите, чтобы у вас возникали проблемы с глобализацией, которые возникнут до того, как вы это поймете!
  • 0
    Это не ужасно, без перерыва в исполнении.
Показать ещё 8 комментариев
9

Если вам нужно обработать как входной файл, так и пользовательский ввод (или что-либо еще из stdin), используйте следующее решение:

#!/bin/bash
exec 3<"$1"
while IFS='' read -r -u 3 line || [[ -n "$line" ]]; do
    read -p "> $line (Press Enter to continue)"
done

На основании принятого ответа и учебника перенаправления bash-хакеров.

Здесь мы открываем дескриптор файла 3 для файла, переданного в качестве аргумента сценария, и говорим read чтобы использовать этот дескриптор в качестве входных данных (-u 3). Таким образом, мы оставляем дескриптор ввода по умолчанию (0), прикрепленный к терминалу или другому источнику ввода, способный считывать ввод пользователя.

7

Для правильной обработки ошибок:

#!/bin/bash

set -Ee    
trap "echo error" EXIT    
test -e ${FILENAME} || exit
while read -r line
do
    echo ${line}
done < ${FILENAME}
  • 0
    Не могли бы вы добавить объяснение?
  • 0
    К сожалению, он пропускает последнюю строку в файле.
Показать ещё 1 комментарий
0

Следующее просто распечатает содержимое файла:

cat $Path/FileName.txt

while read line;
do
echo $line     
done
-9

Я прочитал вопрос как:

", если я хочу прочитать файл, ожидая, как это сделать? Я хочу сделать это, потому что, когда я писал" выполнение некоторых задач с помощью $name ", я имел в виду, что мои задачи - это команды."

Прочитайте файл из самого ожидаемого:

yourExpectScript:

#!/usr/bin/expect
# Pass in filename from command line

set filename [ lindex $argv 0 ]

# Assumption: file in the same directory

set inFile [ open $filename r ]

while { ! [ eof $inFile ] } {

    set line [ gets $inFile ]

    # You could set name directly.

    set name $line

    # Do other expect stuff with $name ...

    puts " Name: $name"
}

close $inFile

Затем назовите его так:

yourExpectScript file_with_names.txt
  • 10
    Это не похоже на Баш.
  • 2
    На самом деле - это TCL (язык, который expect расширяется), который очень не bash.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню