Cách đọc từ hai tệp đầu vào bằng vòng lặp while


27

Tôi muốn biết liệu có cách đọc nào từ hai tệp đầu vào trong một vòng lặp trong khi lặp một dòng tại một thời điểm không. Ví dụ: giả sử tôi có hai tệp FileAFileB.

Tập tin:

[jaypal:~/Temp] cat filea
this is File A line1
this is File A line2
this is File A line3

TệpB:

[jaypal:~/Temp] cat fileb
this is File B line1
this is File B line2
this is File B line3

Tập lệnh mẫu hiện tại:

[jaypal:~/Temp] cat read.sh 
#!/bin/bash
while read lineA
    do echo $lineA 
    while read lineB
        do echo $lineB 
        done < fileb
done < filea

Chấp hành:

[jaypal:~/Temp] ./read.sh 
this is File A line1
this is File B line1
this is File B line2
this is File B line3
this is File A line2
this is File B line1
this is File B line2
this is File B line3
this is File A line3
this is File B line1
this is File B line2
this is File B line3

Vấn đề và đầu ra mong muốn:

Vòng lặp này hoàn toàn trên FileB cho mỗi dòng trong FileA. Tôi đã thử sử dụng tiếp tục, phá vỡ, thoát nhưng không ai trong số chúng có nghĩa là để đạt được đầu ra mà tôi đang tìm kiếm. Tôi muốn tập lệnh chỉ đọc một dòng từ Tệp A và sau đó một dòng từ FileB và thoát khỏi vòng lặp và tiếp tục với dòng thứ hai của Tệp A và dòng thứ hai của Tệp B. Một cái gì đó tương tự với tập lệnh sau -

[jaypal:~/Temp] cat read1.sh 
#!/bin/bash
count=1
while read lineA
    do echo $lineA 
        lineB=`sed -n "$count"p fileb`
        echo $lineB
        count=`expr $count + 1`
done < filea

[jaypal:~/Temp] ./read1.sh 
this is File A line1
this is File B line1
this is File A line2
this is File B line2
this is File A line3
this is File B line3

Điều này có thể đạt được với vòng lặp while không?


Một giải pháp tuyệt vời của @codaddict có tại đây: stackoverflow.com/a/4011824paste -d '\n' file1 file2
4095830

Câu trả lời:


32

Nếu bạn biết chắc chắn rằng một số ký tự sẽ không bao giờ xảy ra trong tệp đầu tiên thì bạn có thể sử dụng dán.

Ví dụ về dán bằng cách sử dụng tab dấu phân cách mặc định:

paste file1 file2 | while IFS="$(printf '\t')" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

Ví dụ về dán bằng cách sử dụng @:

paste -d@ file1 file2 | while IFS="@" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

Lưu ý rằng nó là đủ nếu ký tự được đảm bảo không xảy ra trong tệp đầu tiên. Điều này là do readsẽ bỏ qua IFSkhi điền vào biến cuối cùng. Vì vậy, ngay cả khi @xảy ra trong tập tin thứ hai, nó sẽ không bị chia tách.

Ví dụ về dán bằng cách sử dụng một số tính năng bash cho mã sạch hơn được cho là:

while IFS=$'\t' read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done < <(paste file1 file2)

Các tính năng Bash được sử dụng: ansi c string ( $'\t') và process thay thế ( <(...)) để tránh vòng lặp while trong một vấn đề về subshell .

Nếu bạn không thể chắc chắn rằng bất kỳ ký tự nào sẽ không bao giờ xảy ra trong cả hai tệp thì bạn có thể sử dụng mô tả tệp .

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done 3<file1 4<file2

Không được kiểm tra nhiều. Có thể phá vỡ trên các dòng trống.

Các mô tả tệp số 0, 1 và 2 đã được sử dụng cho stdin, stdout và stderr, tương ứng. Mô tả tập tin từ 3 trở lên là (thường) miễn phí. Hướng dẫn bash cảnh báo từ việc sử dụng các mô tả tệp lớn hơn 9, vì chúng được "sử dụng nội bộ".

Lưu ý rằng các mô tả tệp mở được kế thừa cho các hàm shell và các chương trình bên ngoài. Các chức năng và chương trình kế thừa một bộ mô tả tệp mở có thể đọc từ (và ghi vào) bộ mô tả tệp. Bạn nên cẩn thận để đóng tất cả các mô tả tập tin không cần thiết trước khi gọi một chức năng hoặc chương trình bên ngoài.

Đây là chương trình tương tự như trên với công việc thực tế (in) được tách ra khỏi meta-work (đọc từng dòng từ hai tệp song song).

work() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  work "$f1" "$f2"
done 3<file1 4<file2

Bây giờ chúng tôi giả vờ rằng chúng tôi không có quyền kiểm soát mã công việc và mã đó, vì bất kỳ lý do gì, cố gắng đọc từ mô tả tệp 3.

unknowncode() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
  read -r yoink <&3 && printf 'yoink: %s\n' "$yoink"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  unknowncode "$f1" "$f2"
done 3<file1 4<file2

Đây là một ví dụ đầu ra. Lưu ý rằng dòng thứ hai từ tệp đầu tiên bị "đánh cắp" từ vòng lặp.

f1: file1 line1
f2: file2 line1
yoink: file1 line2
f1: file1 line3
f2: file2 line2

Dưới đây là cách bạn nên đóng bộ mô tả tệp trước khi gọi mã bên ngoài (hoặc bất kỳ mã nào cho vấn đề đó).

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  # this will close fd3 and fd4 before executing anycode
  anycode "$f1" "$f2" 3<&- 4<&-
  # note that fd3 and fd4 are still open in the loop
done 3<file1 4<file2

17

Mở hai tệp trên các mô tả tệp khác nhau . Chuyển hướng đầu vào của phần tích readhợp vào bộ mô tả mà tệp bạn muốn được kết nối. Trong bash / ksh / zsh, bạn có thể viết read -u 3thay vì read <&3.

while IFS= read -r lineA && IFS= read -r lineB <&3; do
  echo "$lineA"; echo "$lineB"
done <fileA 3<fileB

Đoạn mã này dừng khi tệp ngắn nhất đã được xử lý. Xem Đọc hai tệp vào vòng lặp IFS - Có cách nào để có kết quả khác 0 trong trường hợp này không? nếu bạn muốn tiếp tục xử lý cho đến khi kết thúc cả hai tập tin.

Xem thêm Khi nào bạn sẽ sử dụng một mô tả tập tin bổ sung? để biết thêm thông tin về các mô tả tệp và Tại sao `while IFS = read` được sử dụng thường xuyên, thay vì` IFS =; trong khi đọc..`? cho một lời giải thích về IFS= read -r.


Cảm ơn @Gilles cho các liên kết bổ sung trên mô tả tập tin.
jaypal singh

@Gilles có lẽ tôi đã hiểu nhầm bạn, nhưng tôi không thể làm cho vòng lặp xử lý toàn bộ tệp dài nhất (luôn là $ fileA trong trường hợp của tôi), vì vậy tôi đã đặt nó thành một câu hỏi riêng, đó là: có cách nào để viết vòng lặp không mà diff không nhận thấy sự khác biệt giữa đầu vào và đầu ra? unix.stackexchange.com/questions/26780/ Cách gần nhất tôi có thể nhận được là khác nhau chỉ tìm thấy một dòng khác biệt.
ixtmixilix

3

Tôi biết bạn muốn có một kịch bản shell, nhưng bạn có thể muốn xem pastelệnh.


Cảm ơn @lutzky. pastecũng ngầu
jaypal singh

2

Hãy thử lệnh dưới đây:

paste -d '\n' inp1.txt inp2.txt > outfile.txt

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.