it-swarm-vi.com

Làm thế nào để lặp qua các dòng của một tập tin?

Nói rằng tôi có tập tin này:

hello
world
hello world

Chương trình này

#!/bin/bash

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

đầu ra

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

Tôi muốn có for lặp đi lặp lại trên mỗi dòng riêng lẻ bỏ qua các khoảng trắng, ví dụ: hai dòng cuối cùng sẽ được thay thế bằng

tester: hello world

Sử dụng dấu ngoặc kép for i in "$(cat $1)"; dẫn đến i được gán toàn bộ tệp cùng một lúc. Tôi nên thay đổi cái gì?

63
Tobias Kienzler

Với 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

Tuy nhiên, xin lưu ý rằng nó sẽ bỏ qua các dòng trống vì dòng mới là một ký tự khoảng trắng IFS, các chuỗi của nó được tính là 1 và các dòng đầu và cuối là làm ngơ. Với zshksh93 (không phải bash), bạn có thể đổi nó thành IFS=$'\n\n' đối với dòng mới không được xử lý đặc biệt, tuy nhiên lưu ý rằng tất cả các ký tự dòng mới (để bao gồm các dòng trống theo dõi) sẽ luôn bị xóa bởi lệnh thay thế.

Hoặc với read (không còn cat):

#!/bin/bash

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

Ở đó, các dòng trống được giữ nguyên, nhưng lưu ý rằng nó sẽ bỏ qua dòng cuối cùng nếu nó không được phân định chính xác bởi một ký tự dòng mới.

76
wag

Đối với những gì đáng giá, tôi cần phải làm điều đó khá thường xuyên và không bao giờ có thể nhớ chính xác cách sử dụng while IFS= read..., vì vậy tôi đã xác định hàm sau trong hồ sơ bash của mình:

# 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
}

Hàm này trước tiên xác định số lượng dòng trong tệp, sau đó sử dụng sed để trích xuất dòng sau dòng và chuyển từng dòng dưới dạng một đối số chuỗi cho bất kỳ hàm đã cho nào. Tôi cho rằng điều này có thể thực sự không hiệu quả với các tệp lớn, nhưng điều đó không phải là vấn đề đối với tôi cho đến nay (những gợi ý về cách cải thiện sự chào đón này tất nhiên).

Cách sử dụng khá ngọt ngào 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