การเปรียบเทียบสองไฟล์ใน linux terminal


168

มีสองไฟล์ที่เรียกว่า"a.txt"และ"b.txt"ทั้งสองมีรายการคำ ตอนนี้ผมต้องการที่จะตรวจสอบคำที่มีความพิเศษในการ"a.txt"และไม่ได้อยู่ใน"b.txt"

ฉันต้องการอัลกอริทึมที่มีประสิทธิภาพเพราะฉันต้องการเปรียบเทียบพจนานุกรมสองเล่ม


27
diff a.txt b.txtไม่พอ?
ThanksForAllTheFish

คำสามารถเกิดขึ้นได้หลายครั้งในแต่ละไฟล์หรือไม่? คุณสามารถจัดเรียงไฟล์ได้หรือไม่
Basile Starynkevitch

ฉันต้องการเฉพาะคำเหล่านั้นที่ไม่มีใน "b.txt" และมีอยู่ใน a.txt
Ali Imran

คำตอบ:


343

หากคุณติดตั้งเป็นกลุ่มลองสิ่งนี้:

vimdiff file1 file2

หรือ

vim -d file1 file2

คุณจะพบว่ามันยอดเยี่ยมป้อนคำอธิบายรูปภาพที่นี่


9
ยอดเยี่ยมแน่นอนในการออกแบบที่ดีและง่ายต่อการค้นหาความแตกต่าง Ohmygod
Zen

1
คำตอบของคุณยอดเยี่ยม แต่ครูของฉันต้องการให้ฉันไม่ใช้ฟังก์ชันห้องสมุดใด ๆ : P
Ali Imran

1
เป็นเครื่องมือที่ยอดเยี่ยมมาก! สิ่งนี้มีประโยชน์อย่างมาก
user1205577

1
ความหมายของสีเหล่านั้นคืออะไร?
zygimantus

1
รหัสสีหมายความว่าพวกเขาแตกต่างกันในสองไฟล์ @zygimantus
Li

73

จัดเรียงและใช้comm:

comm -23 <(sort a.txt) <(sort b.txt)

commเปรียบเทียบ (เรียงลำดับ) ไฟล์อินพุตและโดยดีฟอลต์เอาต์พุตสามคอลัมน์: บรรทัดที่ไม่ซ้ำกับ a, บรรทัดที่ไม่ซ้ำกับ b และบรรทัดที่มีอยู่ในทั้งสอง โดยการระบุ-1, -2และ / หรือ-3คุณสามารถระงับการส่งออกที่สอดคล้องกัน ดังนั้นcomm -23 a bจะแสดงเฉพาะรายการที่ไม่ซ้ำกับ a ฉันใช้<(...)ไวยากรณ์เพื่อเรียงลำดับไฟล์ในทันทีหากไฟล์เหล่านั้นถูกจัดเรียงแล้วคุณไม่ต้องการสิ่งนี้


ฉันได้เพิ่มคำตอบของตัวเองโดยใช้คำสั่ง grep เท่านั้นโปรดบอกฉันว่ามันมีประสิทธิภาพมากขึ้น?
Ali Imran

3
@AliImran commมีประสิทธิภาพมากขึ้นเพราะทำงานในการรันครั้งเดียวโดยไม่ต้องเก็บไฟล์ทั้งหมดไว้ในหน่วยความจำ เมื่อคุณใช้พจนานุกรมที่เรียงลำดับแล้วคุณไม่จำเป็นต้องใช้sortมัน การใช้grep -f file1 file2ในทางกลับกันจะโหลดทั้งfile1ในหน่วยความจำและเปรียบเทียบในแต่ละบรรทัดfile2ที่มีทั้งหมดของรายการเหล่านั้นซึ่งมีประสิทธิภาพมากน้อย -f file1มันมีประโยชน์ส่วนใหญ่มีขนาดเล็กไม่ได้เรียงลำดับ
Anders Johansson

1
ขอบคุณ @AndersJohansson ที่แชร์คำสั่ง "comm" มันช่างดีเหลือเกิน ฉันมักจะต้องทำการรวมตัวกันด้านนอกระหว่างไฟล์และนี่ก็เป็นเคล็ดลับ
blispr

ให้ความสนใจกับตัวละครบรรทัดใหม่ ... ฉันเพิ่งพบว่า\nจะรวมอยู่ในการเปรียบเทียบ
Bin


28

คุณสามารถใช้diffเครื่องมือใน linux เพื่อเปรียบเทียบสองไฟล์ คุณสามารถใช้--changed กลุ่มรูปแบบและ--unchanged กลุ่มรูปแบบตัวเลือกในการกรองข้อมูลที่จำเป็น

สามตัวเลือกต่อไปนี้สามารถใช้เพื่อเลือกกลุ่มที่เกี่ยวข้องสำหรับแต่ละตัวเลือก:

  • '% <' รับสายจาก FILE1

  • '%>' รับสายจาก FILE2

  • '' (สตริงว่าง) เพื่อลบบรรทัดออกจากไฟล์ทั้งสอง

เช่น: diff --changed-group-format = "% <" --unchanged-group-format = "" file1.txt file2.txt

[root@vmoracle11 tmp]# cat file1.txt 
test one
test two
test three
test four
test eight
[root@vmoracle11 tmp]# cat file2.txt 
test one
test three
test nine
[root@vmoracle11 tmp]# diff --changed-group-format='%<' --unchanged-group-format='' file1.txt file2.txt 
test two
test four
test eight

27

หากคุณต้องการสไตล์เอาต์พุตที่ต่างจากgit diffคุณสามารถใช้กับ--no-indexแฟล็กเพื่อเปรียบเทียบไฟล์ที่ไม่อยู่ในที่เก็บ git:

git diff --no-index a.txt b.txt

ด้วยการใช้ไฟล์สองสามไฟล์ที่มีสตริงชื่อไฟล์ประมาณ 200k ในแต่ละครั้งฉันเปรียบเทียบ (ด้วยtimeคำสั่งในตัว) วิธีนี้เทียบกับคำตอบอื่น ๆ ที่นี่:

git diff --no-index a.txt b.txt
# ~1.2s

comm -23 <(sort a.txt) <(sort b.txt)
# ~0.2s

diff a.txt b.txt
# ~2.6s

sdiff a.txt b.txt
# ~2.7s

vimdiff a.txt b.txt
# ~3.2s

commดูเหมือนจะเร็วที่สุดในขณะที่git diff --no-indexดูเหมือนจะเป็นวิธีที่เร็วที่สุดสำหรับเอาท์พุตสไตล์


อัปเดต 2018-03-25คุณสามารถละเว้นการ--no-indexตั้งค่าสถานะได้เว้นแต่ว่าคุณอยู่ในที่เก็บ git และต้องการเปรียบเทียบไฟล์ที่ไม่ได้ติดตามภายในที่เก็บข้อมูลนั้น จากหน้าคน :

แบบฟอร์มนี้คือการเปรียบเทียบสองเส้นทางที่กำหนดบนระบบไฟล์ คุณสามารถละเว้นตัวเลือก --no-index เมื่อเรียกใช้คำสั่งในแผนผังการทำงานที่ควบคุมโดย Git และอย่างน้อยหนึ่งเส้นทางชี้ไปที่ด้านนอกแผนผังการทำงานหรือเมื่อเรียกใช้คำสั่งภายนอกแผนผังการทำงานที่ควบคุมโดย Git




4

ใช้comm -13 (ต้องการไฟล์ที่เรียงลำดับ) :

$ cat file1
one
two
three

$ cat file2
one
two
three
four

$ comm -13 <(sort file1) <(sort file2)
four

1

นี่คือทางออกของฉันสำหรับสิ่งนี้:

mkdir temp
mkdir results
cp /usr/share/dict/american-english ~/temp/american-english-dictionary
cp /usr/share/dict/british-english ~/temp/british-english-dictionary
cat ~/temp/american-english-dictionary | wc -l > ~/results/count-american-english-dictionary
cat ~/temp/british-english-dictionary | wc -l > ~/results/count-british-english-dictionary
grep -Fxf ~/temp/american-english-dictionary ~/temp/british-english-dictionary > ~/results/common-english
grep -Fxvf ~/results/common-english ~/temp/american-english-dictionary > ~/results/unique-american-english
grep -Fxvf ~/results/common-english ~/temp/british-english-dictionary > ~/results/unique-british-english

2
คุณลองใช้วิธีแก้ไขปัญหาอื่น ๆ หรือไม่? หนึ่งในโซลูชันเหล่านี้มีประโยชน์กับคุณหรือไม่? คำถามของคุณเป็นเรื่องธรรมดาพอที่จะดึงดูดผู้ใช้จำนวนมาก แต่คำตอบของคุณมีความเฉพาะเจาะจงมากขึ้นสำหรับรสนิยมของฉัน ... สำหรับกรณีของฉันsdiff -s file1 file2มีประโยชน์
Metafaniel

@Metafaniel วิธีการแก้ปัญหาของฉันไม่ได้ใช้คำสั่ง sdiff ใช้เพียงคำสั่ง linux ในตัวเพื่อแก้ไขปัญหา
Ali Imran

-1

ใช้ awk สำหรับมัน ทดสอบไฟล์:

$ cat a.txt
one
two
three
four
four
$ cat b.txt
three
two
one

The awk:

$ awk '
NR==FNR {                    # process b.txt  or the first file
    seen[$0]                 # hash words to hash seen
    next                     # next word in b.txt
}                            # process a.txt  or all files after the first
!($0 in seen)' b.txt a.txt   # if word is not hashed to seen, output it

รายการที่ซ้ำกันจะถูกส่งออก:

four
four

หากต้องการหลีกเลี่ยงคำที่ซ้ำกันให้เพิ่มคำที่พบใหม่แต่ละคำใน a.txt เพื่อseenแฮช:

$ awk '
NR==FNR {
    seen[$0]
    next
}
!($0 in seen) {              # if word is not hashed to seen
    seen[$0]                 # hash unseen a.txt words to seen to avoid duplicates 
    print                    # and output it
}' b.txt a.txt

เอาท์พุท:

four

หากรายการคำคั่นด้วยเครื่องหมายจุลภาคเช่น:

$ cat a.txt
four,four,three,three,two,one
five,six
$ cat b.txt
one,two,three

คุณต้องทำสองรอบพิเศษ ( forลูป):

awk -F, '                    # comma-separated input
NR==FNR {
    for(i=1;i<=NF;i++)       # loop all comma-separated fields
        seen[$i]
    next
}
{
    for(i=1;i<=NF;i++)
        if(!($i in seen)) {
             seen[$i]        # this time we buffer output (below):
             buffer=buffer (buffer==""?"":",") $i
        }
    if(buffer!="") {         # output unempty buffers after each record in a.txt
        print buffer
        buffer=""
    }
}' b.txt a.txt

ส่งออกเวลานี้:

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