วนรอบสองบรรทัดของไฟล์แบบขนาน [ปิด]


18

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

while read compareFile <&3; do     
 if [[ ! $server =~ [^[:space:]] ]] ; then  #empty line exception
    continue
 fi   
    echo "Comparing file - $compareFile"
 if diff "$compareFile" _(other file from loop?_) >/dev/null ; then
     echo Same
 else
      echo Different
 fi 
done 3</infanass/dev/admin/filestoCompare.txt

ฉันต้องสามารถเปรียบเทียบไฟล์จากสองรายการที่แตกต่างกันในเวลาเดียวกันถึงสองในขณะที่อ่านลูป ... มันเป็นไปได้หรือไม่


คุณตั้งใจจะทำให้บทนี้เป็นราชาแห่งความท้าทายหรือไม่? diffถ้าไม่ได้ดังนั้นเครื่องมือที่มีประสิทธิภาพอยู่แล้วที่จะเปรียบเทียบไฟล์เช่น
lgeorget

" ชนิดของความท้าทาย" ขอโทษ
lgeorget

@lgeorget OP กำลังใช้งานdiffอยู่
terdon

อ่าไฟล์จากสองรายการ ขออภัยสำหรับความคิดเห็นที่ไร้ประโยชน์ ...
lgeorget

โปรดหลีกเลี่ยงการโพสต์ข้าม
iruvar

คำตอบ:


20

คุณไม่ต้องการสองลูป คุณเพียงแค่ต้องอ่านจากสองไฟล์ในหนึ่งวง

while read compareFile1 <&3 && read compareFile2 <&4; do     
 if [[ ! $server =~ [^[:space:]] ]] ; then  #empty line exception
    continue
 fi   
    echo "Comparing file - $compareFile"
 if diff "$compareFile1" "$compareFile2" >/dev/null ; then
     echo Same
 else
      echo Different
 fi 
done 3</infanass/dev/admin/filestoCompare.txt 4<other_file

ขอบคุณมากรหัสน้อยกว่ามาก! ฉันจะจัดการกับข้อยกเว้นบรรทัดว่างในเวลาเดียวกันแล้วสำหรับทั้งสองลูปได้อย่างไร
mkrouse

@ mkrouse ฉันไม่ทราบว่าคุณกำลังทำอะไรกับตัวแปร $ server ที่นั่นมาก่อน แต่อย่างไรก็ตามคุณทดสอบบรรทัดว่างบนตัวแปรหนึ่งคุณเพียงแค่ทำสิ่งเดียวกันกับที่อื่น ...
psusi

7

วิธีที่ 1: ใช้สิ่งที่คุณรู้

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

paste -- "$list1.txt" "list2.txt" |
while IFS=$'\t' read -r file1 file2 rest; do
  diff -q -- "$file1" "$file2"
  case $? in
    0) status='same';;
    1) status='different';;
    *) status='ERROR';;
  esac
  echo "$status $file1 $file2"
done

หากคุณต้องการข้ามบรรทัดว่างคุณต้องทำในแต่ละไฟล์แยกกันเนื่องจากpasteอาจจับคู่บรรทัดว่างจากไฟล์หนึ่งกับบรรทัดที่ไม่ว่างจากไฟล์อื่น คุณสามารถใช้grepในการกรองบรรทัดที่ไม่ว่างเปล่า

paste -- <(grep '[^[:space:]]' "$list1.txt") <(grep '[^[:space:]]' "list2.txt") |
while IFS=$'\t' read -r file1 file2 rest; do
  

โปรดทราบว่าหากไฟล์ทั้งสองมีความยาวแตกต่างกันคุณจะว่างเปล่า$file2(ไม่ว่ารายการใดจะสิ้นสุดก่อน)

วิธีที่ 2: วนซ้ำสองไฟล์

คุณสามารถวางคำสั่งที่ซับซ้อนตามที่คุณต้องการในเงื่อนไขของลูป while หากคุณใส่read file1 <&3 && read file2 <&4ลูปแล้วจะทำงานตราบใดที่ไฟล์ทั้งคู่มีบรรทัดที่จะอ่านเช่นจนกว่าไฟล์หนึ่งไฟล์จะหมด

while read -u 3 -r file1 && read -u 4 -r file2; do
  
done 3<list1..txt 4<list2.txt

หากคุณต้องการข้ามผ่านบรรทัดว่างมันซับซ้อนกว่านี้เล็กน้อยเนื่องจากคุณต้องทำการข้ามในสองไฟล์อย่างอิสระ วิธีที่ง่ายคือการแบ่งปัญหาออกเป็นสองส่วน: ข้ามบรรทัดว่างจากไฟล์เดียวและประมวลผลบรรทัดที่ไม่ว่าง วิธีหนึ่งในการข้ามบรรทัดว่างคือการดำเนินการgrepตามข้างต้น ระวังพื้นที่ที่จำเป็นระหว่าง<โอเปอเรเตอร์การเปลี่ยนเส้นทางและ<(คำสั่งที่เริ่มต้น susbtitution

while read -u 3 -r file1 && read -u 4 -r file2; do
  
done 3< <(grep '[^[:space:]]' "$list1.txt") 4< <(grep '[^[:space:]]' "list2.txt")

อีกวิธีหนึ่งคือการเขียนฟังก์ชั่นที่ทำงานเหมือนreadแต่ข้ามบรรทัดว่าง ฟังก์ชั่นนี้สามารถทำงานได้โดยการโทรreadเป็นลูป มันไม่จำเป็นต้องเป็นฟังก์ชั่น แต่ฟังก์ชั่นเป็นวิธีที่ดีที่สุดทั้งในการจัดระเบียบรหัสของคุณและเพราะต้องเรียกชิ้นส่วนของรหัสนั้นสองครั้ง ในฟังก์ชั่น${!#}เป็นตัวอย่างของการสร้างทุบตี${!VARIABLE}ซึ่งประเมินมูลค่าของตัวแปรที่มีชื่อเป็นค่าของVARIABLE; นี่คือตัวแปรเป็นตัวแปรพิเศษ#ที่มีจำนวนพารามิเตอร์ตำแหน่งดังนั้นจึง${!#}เป็นพารามิเตอร์ตำแหน่งสุดท้าย

function read_nonblank {
  while read "$@" &&
        [[ ${!#} !~ [^[:space:]] ]]
  do :; done
}
while read_nonblank -u 3 -r file1 && read_nonblank -u 4 -r file2; do
  
done 3<list1..txt 4<list2.txt

ฉันชอบการใช้-uตัวเลือกของการอ่าน
เฟลิเป้อัลวาเรซ

1

วิธีการหนึ่งที่จะใช้แทนเพียงread -ra readสมมติว่าfilestoCompare.txtมี 2 คอลัมน์ที่มีชื่อไฟล์ในแต่ละครั้งจะอ่านคอลัมน์ทั้งในในเวลาเดียวกันและกำหนดให้เป็นอาร์เรย์,read -ra compareFileอาร์เรย์นี้สามารถเข้าถึงได้เพื่อให้ดัชนี 0 เป็นไฟล์แรกและดัชนี 1 จะเป็นไฟล์ที่ 2 ในแต่ละครั้งผ่านwhileลูป

ตัวอย่าง

ว่าฉันมีไฟล์นี้: filestoCompare.txtและมันมีดังต่อไปนี้:

file1 file2
file3 file4
file5 file6

คำสั่งที่ใช้ในไฟล์นี้จะเป็นดังนี้:

$ while read -ra a ; do printf "%s\t%s\n" ${a[0]} ${a[1]}; done < filestoCompare.txt
file1   file2
file3   file4
file5   file6

หากไฟล์ทั้งสองเป็นไฟล์ที่แยกต่างหากเช่น:

#list1
file1
file2
file3

#list2
file4
file5
file6

พวกเขาสามารถเข้าร่วมกับpasteคำสั่งดังนี้:

$ paste list1 list2 > list1and2

นี่คือเนื้อหาของ list1and2:

$ cat list1and2
file1   file4
file2   file5
file3   file6

แต่นั่นไม่ใช่รูปแบบอินพุต: รายการอยู่ในสองไฟล์ที่ต่างกัน คุณสามารถทำได้joinก่อน
Gilles 'หยุดความชั่วร้าย'

@Gilles - ฉันรู้ว่านั่นไม่ใช่รูปแบบอินพุตฉันเชื่อว่าฉันยังพูดอีกว่า "... สมมติว่า filestoCompare.txt มี 2 คอลัมน์พร้อมชื่อไฟล์ในแต่ละ ... " ฉันเข้าใจการโต้แย้งของคุณและไม่เห็นด้วย OP ไม่ได้ให้คำแนะนำเพิ่มเติมเกี่ยวกับคำถามนี้ตั้งแต่โพสต์
slm

@Gilles - ถ้าฉันเพิ่มตัวอย่างแสดงวิธีใช้คำสั่งpasteเพื่อเข้าร่วม 2 ไฟล์? นั่นจะทำให้คุณยกเลิกการลงคะแนนเสียงหรือไม่?
slm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.