Как перебрать строки файла?

61

Скажи, у меня есть этот файл:

hello
world
hello world

Эта программа

#!/bin/bash

for i in $(cat $1); do
    echo "tester: $i"
done

выходы

tester: hello
tester: world
tester: hello
tester: world

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

tester: hello world

Использование кавычек for i in "$(cat $1)";приводит iк назначению всего файла за один раз. Что я должен изменить?

Тобиас Кинцлер
источник

Ответы:

69

С forи ИФС :

#!/bin/bash

IFS=$'\n'       # make newlines the only separator
set -f          # disable globbing
for i in $(cat < "$1"); do
  echo "tester: $i"
done

Однако обратите внимание, что он пропускает пустые строки, так как символ новой строки является символом IFS-пробела, его последовательности считаются за 1, а начальные и конечные игнорируются. С помощью zshи ksh93(не bash) вы можете изменить его на символ IFS=$'\n\n'новой строки, который не будет обрабатываться специально, однако обратите внимание, что все завершающие символы новой строки (включая завершающие пустые строки) всегда будут удаляться подстановкой команды.

Или сread (не более cat):

#!/bin/bash

while IFS= read -r line; do
  echo "tester: $line"
done < "$1"

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

шутник
источник
5
спасибо, я не знал, что это можно <сделать целым циклом. Хотя теперь это
вполне логично,
1
Я вижу IFS \ read -r line' in second example. Is really IFS = `необходимо? ИМХО этого достаточно, чтобы сказать:while read -r line; do echo "tester: $line"; done < "$1"
Гжегож Вежовецкий
4
@GrzegorzWierzowiecki IFS=отключает удаление начальных и конечных пробелов. См В while IFS= read.., почему не делает МФС никакого эффекта?
Жиль "ТАК - перестать быть злым"
0

Что бы это ни стоило, мне нужно делать это довольно часто, и я никогда не могу вспомнить точный способ использования while IFS= read..., поэтому я определил следующую функцию в своем профиле bash:

# iterate the line of a file and call input function
iterlines() {
    (( $# < 2 )) && { echo "Usage: iterlines <File> <Callback>"; return; }
    local File=$1
    local Func=$2
    n=$(cat "$File" | wc -l)
    for (( i=1; i<=n; i++ )); do
        "$Func" "$(sed "${i}q;d" "$File")"
    done
}

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

Использование довольно сладкое ИМО:

>> cat example.txt # note the use of spaces, whitespace, etc.
a/path

This is a sentence.
"wi\th quotes"
$End
>> iterlines example.txt echo # preserves quotes, $ and whitespace
a/path

This is a sentence.
"wi\th quotes"
$End
>> x() { echo "$#"; }; iterlines example.txt x # line always passed as single input string
1
1 
1
1
1
Sheljohn
источник