Як перевести цикл на рядки файлу?


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та IFS :

#!/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.., чому IFS не має ефекту?
Жиль

0

Для чого це потрібно робити мені досить часто, і я ніколи не пам'ятаю точний спосіб використання while IFS= read..., тому я визначив наступну функцію в своєму баш-профілі:

# 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.