วิธีอ่านจากสองไฟล์อินพุตที่ใช้ขณะลูป


27

ฉันต้องการทราบว่ามีวิธีการอ่านใด ๆ จากไฟล์อินพุตสองไฟล์ในแบบซ้อนในขณะที่วนรอบทีละหนึ่งบรรทัด ยกตัวอย่างเช่นช่วยบอกฉันมีสองไฟล์และFileAFileB

FileA:

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

FileB:

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

สคริปต์ตัวอย่างปัจจุบัน:

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

การดำเนินการ:

[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

ปัญหาและผลลัพธ์ที่ต้องการ:

นี่เป็นการวนลูปมากกว่า FileB สำหรับแต่ละบรรทัดใน FileA ฉันลองใช้ Continue, break, exit แต่ไม่มีสิ่งใดที่มีไว้เพื่อให้ได้ผลลัพธ์ที่ฉันต้องการ ฉันต้องการสคริปต์เพื่ออ่านเพียงหนึ่งบรรทัดจากไฟล์ A จากนั้นหนึ่งบรรทัดจาก FileB และออกจากลูปและดำเนินการต่อด้วยบรรทัดที่สองของไฟล์ A และบรรทัดที่สองของไฟล์ B. บางสิ่งที่คล้ายกับสคริปต์ต่อไปนี้ -

[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

เป็นไปได้ที่จะประสบความสำเร็จในขณะที่วนซ้ำ?


ทางออกที่ยอดเยี่ยมโดย@codaddictอยู่ที่นี่: stackoverflow.com/a/4011824/4095830 ->paste -d '\n' file1 file2
whoan

คำตอบ:


32

หากคุณรู้ว่าตัวอักษรบางตัวจะไม่เกิดขึ้นในไฟล์แรกคุณสามารถใช้แปะได้

ตัวอย่างของการวางโดยใช้แท็บตัวคั่นเริ่มต้น:

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

ตัวอย่างการวางโดยใช้@:

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

โปรดทราบว่ามันเพียงพอหากตัวละครมีการประกันว่าจะไม่เกิดขึ้นในไฟล์แรก นี่เป็นเพราะreadจะไม่สนใจIFSเมื่อเติมตัวแปรสุดท้าย ดังนั้นแม้ว่าจะ@เกิดขึ้นในไฟล์ที่สองมันจะไม่ถูกแยกออก

ตัวอย่างของการวางโดยใช้คุณลักษณะทุบตีบางอย่างสำหรับโค้ดที่ชัดเจนกว่า:

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

ทุบตีคุณสมบัติการใช้ANSI คสตริง ( $'\t') และขั้นตอนการเปลี่ยนตัว ( <(...)) เพื่อหลีกเลี่ยงการห่วงในขณะที่ปัญหา subshell

หากคุณไม่แน่ใจว่าตัวละครใด ๆ จะไม่เกิดขึ้นในทั้งสองไฟล์คุณสามารถใช้ตัวอธิบายไฟล์ได้

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

ทดสอบไม่มาก อาจทำลายเส้นที่ว่างเปล่า

ตัวอธิบายไฟล์หมายเลข 0, 1 และ 2 ถูกใช้สำหรับ stdin, stdout และ stderr แล้วตามลำดับ ตัวอธิบายไฟล์ตั้งแต่ 3 ขึ้นไปเป็นอิสระ (ปกติ) คู่มือ bash เตือนไม่ให้ใช้ file descriptor ที่มากกว่า 9 เพราะเป็น "ใช้ภายใน"

โปรดทราบว่าตัวอธิบายไฟล์แบบเปิดได้รับการสืบทอดไปยังฟังก์ชันเชลล์และโปรแกรมภายนอก ฟังก์ชั่นและโปรแกรมที่สืบทอด descriptor เปิดไฟล์สามารถอ่าน (และเขียน) descriptor ไฟล์ คุณควรระมัดระวังในการปิดไฟล์ descriptor ทั้งหมดที่ไม่จำเป็นก่อนที่จะเรียกใช้ฟังก์ชั่นหรือโปรแกรมภายนอก

นี่คือโปรแกรมเดียวกันกับข้างบนที่มีงานจริง (การพิมพ์) แยกออกจาก meta-work (อ่านทีละบรรทัดจากไฟล์สองไฟล์ในแบบคู่ขนาน)

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

ตอนนี้เราทำเป็นว่าเราไม่สามารถควบคุมรหัสงานและรหัสนั้นได้ไม่ว่าด้วยเหตุผลใดก็ตามพยายามอ่านจากไฟล์อธิบาย 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

นี่คือตัวอย่างเอาต์พุต โปรดทราบว่าบรรทัดที่สองจากไฟล์แรกคือ "ถูกขโมย" จากลูป

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

นี่คือวิธีที่คุณควรปิดตัวอธิบายไฟล์ก่อนเรียกรหัสภายนอก (หรือรหัสใด ๆ สำหรับเรื่องนั้น)

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

เปิดแฟ้มที่สองในการที่แตกต่างกันอธิบายไฟล์ เปลี่ยนทิศทางอินพุตของreadบิวด์อินไปที่ descriptor ที่ไฟล์ที่คุณต้องการเชื่อมต่ออยู่ ในทุบตี / ksh / zsh คุณสามารถเขียนแทนread -u 3read <&3

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

ตัวอย่างนี้จะหยุดเมื่อไฟล์ที่สั้นที่สุดได้รับการประมวลผล ดูที่การอ่านสองไฟล์ใน IFS ขณะวนซ้ำ - มีวิธีรับผลต่างศูนย์ในกรณีนี้หรือไม่? หากคุณต้องการประมวลผลต่อไปจนสิ้นสุดไฟล์ทั้งสอง

ดูเพิ่มเติมเมื่อใดคุณจะใช้ตัวอธิบายไฟล์เพิ่มเติม สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ file descriptors และทำไมจึงเป็น `ในขณะที่ IFS = read` ใช้บ่อยๆแทนที่จะเป็น IFS =; ในขณะที่อ่าน .. IFS= read -rสำหรับคำอธิบายของ


ขอบคุณ @Gilles สำหรับลิงก์เพิ่มเติมเกี่ยวกับ file descriptor
jaypal singh

@Gilles บางทีฉันเข้าใจผิดคุณ แต่ฉันไม่สามารถทำให้กระบวนการวนรอบเป็นไฟล์ที่ยาวที่สุดได้ทั้งหมด (ซึ่งเป็น $ fileA ในกรณีของฉันเสมอ) ดังนั้นฉันจึงทำสิ่งนั้นเป็นคำถามที่แยกต่างหากเนื่องจาก: มีวิธีเขียนห่วงดังนั้น diff ที่ไม่สังเกตเห็นความแตกต่างระหว่างอินพุตและเอาต์พุตใด ๆ unix.stackexchange.com/questions/26780/…สิ่งที่ใกล้เคียงที่สุดที่ฉันจะได้รับคือ diff ต่างกันเพียงการค้นหาความแตกต่างหนึ่งบรรทัด
ixtmixilix

3

ฉันรู้ว่าคุณต้องการเชลล์สคริปต์ แต่คุณอาจต้องการดูpasteคำสั่ง


ขอบคุณ @lutzky pasteก็เท่ห์เช่นกัน
jaypal singh


0

หรือฉันคิดว่าคุณสามารถ slurp ไฟล์ลงในตัวแปร array โดยผูกแต่ละบรรทัดของไฟล์ลงใน array [line_of_file_index] โดยใช้คำสั่ง mapfile ของ bash อย่างไรก็ตามฉันไม่แน่ใจว่าเป็นเพียงสำหรับ Bash3 ที่สูงกว่าหรือ Bash4

http://wiki.bash-hackers.org/commands/builtin/mapfile

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.