web-dev-qa-db-ja.com

ファイルの行をループする方法は?

このファイルがあるとしましょう:

_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反復処理したいのですが、最後の2行を次のように置き換えます。

_tester: hello world
_

引用符for i in "$(cat $1)";を使用すると、iにファイル全体が一度に割り当てられます。何を変更すればよいですか?

63
Tobias Kienzler

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

ただし、newlineはIFSの空白文字であるため、空の行はスキップされ、そのシーケンスは1としてカウントされ、先頭と末尾の行は無視されました。 zshおよびksh93bashではない)を使用すると、改行を特別に扱わないようにIFS=$'\n\n'に変更できますが、すべてtrailing改行文字(後続の空行を含む)は、常にコマンド置換によって削除されます。

または with read (no_no cat):

#!/bin/bash

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

そこでは空の行が保持されますが、改行文字で適切に区切られていなければ、最後の行をスキップすることに注意してください。

76
wag

それだけの価値があるので、私は頻繁にそれを行う必要があり、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を使用して1行ずつ抽出し、各行を単一の文字列引数として任意の関数に渡します。これは大きなファイルでは本当に非効率になるかもしれないと思いますが、今のところ問題はありません(もちろん、この歓迎を改善する方法についての提案)。

使い方はかなり甘いIMO:

>> 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
1
Jonathan H