ฉันแน่ใจว่าฉันเคยพบคำสั่ง unix ซึ่งสามารถพิมพ์บรรทัดทั่วไปจากไฟล์สองไฟล์ขึ้นไปไม่มีใครรู้จักชื่อของมัน มันง่ายกว่าdiff
มาก
pr-123-xy-45
และ file2 ec11_orop_pr-123-xy-45.gz
มี ฉันต้องการ file3 ที่มีec11_orop_pr-123-xy-45.gz
ฉันแน่ใจว่าฉันเคยพบคำสั่ง unix ซึ่งสามารถพิมพ์บรรทัดทั่วไปจากไฟล์สองไฟล์ขึ้นไปไม่มีใครรู้จักชื่อของมัน มันง่ายกว่าdiff
มาก
pr-123-xy-45
และ file2 ec11_orop_pr-123-xy-45.gz
มี ฉันต้องการ file3 ที่มีec11_orop_pr-123-xy-45.gz
คำตอบ:
comm
คำสั่งที่คุณกำลังมองหาคือ เช่น:-
comm -12 1.sorted.txt 2.sorted.txt
ที่นี่:
-1 : ระงับคอลัมน์ 1 (บรรทัดที่ไม่ซ้ำกับ 1.sorted.txt)
-2 : ระงับคอลัมน์ 2 (บรรทัดที่ไม่ซ้ำกับ 2.sorted.txt)
grep
ทำสิ่งแปลก ๆ ที่คุณอาจไม่คาดหวัง โดยเฉพาะทุกอย่างใน1.txt
จะถูกตีความว่าเป็นนิพจน์ปกติและไม่ใช่สตริงธรรมดา นอกจากนี้ใด ๆ ในบรรทัดว่างจะตรงกับสายทั้งหมดใน1.txt
2.txt
ดังนั้นgrep
จะทำงานในสถานการณ์ที่เฉพาะเจาะจงมากเท่านั้น อย่างน้อยคุณก็ต้องการที่จะใช้fgrep
(หรือgrep -f
) แต่สิ่งที่ว่างเปล่าอาจจะทำให้เกิดความเสียหายในกระบวนการนี้
grep -F -x -f file1 file2
การใช้งาน
comm
คำสั่งเป็น 3 ไฟล์แยกกันได้อย่างไร คำตอบนั้นใหญ่เกินไปที่จะใส่ลงไปอย่างสะดวกสบายที่นี่
หากต้องการใช้คำสั่งcommกับไฟล์ที่ไม่ได้เรียงลำดับอย่างง่ายดายให้ใช้การทดแทนกระบวนการของ Bash :
$ bash --version
GNU bash, version 3.2.51(1)-release
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat > abc
123
567
132
$ cat > def
132
777
321
ดังนั้นไฟล์ abc และ def จึงมีหนึ่งบรรทัดเหมือนกันดังนั้นบรรทัดที่มี "132" การใช้commบนไฟล์ที่ไม่เรียงลำดับ:
$ comm abc def
123
132
567
132
777
321
$ comm -12 abc def # No output! The common line is not found
$
บรรทัดสุดท้ายไม่มีเอาต์พุตไม่พบบรรทัดทั่วไป
ตอนนี้ใช้commบนไฟล์ที่เรียงลำดับแล้วเรียงลำดับไฟล์ด้วยการทดแทนกระบวนการ:
$ comm <( sort abc ) <( sort def )
123
132
321
567
777
$ comm -12 <( sort abc ) <( sort def )
132
ตอนนี้เราได้รับ 132 บรรทัด!
sort abc > abc.sorted
, sort dev > def.sorted
แล้วcomm -12 abc.sorted def.sorted
?
เพื่อเสริม Perl หนึ่งซับนี่คือมันawk
เทียบเท่า:
awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2
การทำเช่นนี้จะอ่านทุกบรรทัดจากfile1
เข้าไปในอาร์เรย์arr[]
จากนั้นตรวจสอบแต่ละบรรทัดfile2
ว่ามีอยู่แล้วภายในอาร์เรย์ (เช่นfile1
) file2
เส้นที่พบจะได้รับการตีพิมพ์ในลำดับที่ปรากฏใน โปรดทราบว่าการเปรียบเทียบin arr
ใช้ทั้งบรรทัดจากfile2
เป็นดัชนีไปยังอาร์เรย์ดังนั้นจะรายงานเฉพาะการจับคู่ที่ตรงกันทั้งหมด
perl
เพราะคนอื่น) ขอบคุณล้านคุณ
บางทีคุณอาจจะหมายถึงcomm
?
เปรียบเทียบไฟล์ที่เรียงลำดับ FILE1 และ FILE2 ทีละบรรทัด
หากไม่มีตัวเลือกให้สร้างเอาต์พุตสามคอลัมน์ คอลัมน์หนึ่งมีบรรทัดที่ไม่ซ้ำกับ FILE1 คอลัมน์ที่สองมีบรรทัดที่ไม่ซ้ำกับ FILE2 และคอลัมน์ที่สามมีบรรทัดที่ใช้ร่วมกันกับทั้งสองไฟล์
ความลับในการค้นหาข้อมูลเหล่านี้คือหน้าข้อมูล สำหรับโปรแกรม GNU พวกเขามีรายละเอียดมากกว่า man-pages ลองinfo coreutils
แล้วมันจะแสดงรายการสิ่งของที่เป็นประโยชน์เล็ก ๆ ทั้งหมดให้กับคุณ
ในขณะที่
grep -v -f 1.txt 2.txt > 3.txt
ให้ความแตกต่างของสองไฟล์ (คืออะไรใน 2.txt และไม่ใช่ใน 1.txt) คุณสามารถทำได้อย่างง่ายดาย
grep -f 1.txt 2.txt > 3.txt
เพื่อรวบรวมบรรทัดทั่วไปทั้งหมดซึ่งควรให้วิธีแก้ไขปัญหาของคุณได้ง่าย หากคุณมีไฟล์ที่เรียงลำดับคุณควรดำเนินการcomm
ต่อไป ความนับถือ!
grep
ทำสิ่งแปลก ๆ ที่คุณอาจไม่คาดคิด โดยเฉพาะทุกอย่างใน1.txt
จะถูกตีความว่าเป็นนิพจน์ปกติและไม่ใช่สตริงธรรมดา นอกจากนี้ใด ๆ ในบรรทัดว่างจะตรงกับสายทั้งหมดใน1.txt
2.txt
ดังนั้นสิ่งนี้จะใช้ได้เฉพาะในสถานการณ์ที่เฉพาะเจาะจงเท่านั้น
grep
สัญลักษณ์POSIX ซึ่งได้รับการสนับสนุนโดยgrep
พบในตัวแปร Unix ที่ทันสมัยที่สุด เพิ่ม-F
(หรือใช้fgrep
) เพื่อระงับนิพจน์ทั่วไป เพิ่ม-x
(ตรงทั้งหมด) เพื่อจับคู่ทั้งบรรทัดเท่านั้น
comm
ไฟล์ที่เรียงลำดับ?
comm
สามารถทำงานกับไฟล์ขนาดใหญ่โดยพลการตราบใดที่พวกมันถูกเรียงลำดับเพราะมันจำเป็นต้องเก็บสามบรรทัดในหน่วยความจำเท่านั้น (ฉันเดาว่า GNU comm
จะรู้ว่าจะเก็บไว้เป็นคำนำหน้าถ้าบรรทัดยาวจริงๆ) grep
วิธีการแก้ปัญหาความต้องการที่จะให้ทุกการแสดงออกการค้นหาในหน่วยความจำ
หากไฟล์ทั้งสองยังไม่ได้จัดเรียงคุณสามารถใช้:
comm -12 <(sort a.txt) <(sort b.txt)
และมันจะทำงานหลีกเลี่ยงข้อผิดพลาดเมื่อทำcomm: file 2 is not in sorted order
comm -12 a.txt b.txt
<(command)
ไม่สามารถเคลื่อนย้ายไปยัง POSIX เชลล์ได้แม้ว่าจะใช้งานได้ใน Bash และอื่น ๆ
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' file1 file2
comm
คำสั่งเป็นจะค้นหาแต่ละบรรทัดfile1
ในfile2
ที่comm
จะเปรียบเทียบเฉพาะในกรณีที่สายn
ในfile1
เท่ากับสายในn
file2
comm
ไม่เพียงเปรียบเทียบ line N ใน file1 กับ line N ใน file2 มันสามารถจัดการชุดของบรรทัดที่แทรกไว้ในไฟล์ใดไฟล์หนึ่งได้อย่างสมบูรณ์แบบ (ซึ่งเทียบเท่ากับการลบชุดของบรรทัดจากไฟล์อื่น ๆ แน่นอน) มันแค่ต้องการอินพุตที่จะเรียงตามลำดับ
comm
คำตอบหากต้องการรักษาความสงบเรียบร้อย ดีกว่าawk
ตอบถ้าไม่มีใครต้องการทำซ้ำ
awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2
ใน Linux รุ่น จำกัด (เช่น QNAP (NAS) ฉันทำงานอยู่):
grep -f file1 file2
สามารถทำให้เกิดปัญหาบางอย่างตามที่ @ChristopherSchultz พูดและการใช้งานgrep -F -f file1 file2
ช้ามาก (มากกว่า 5 นาที - ยังไม่เสร็จ - มากกว่า 2-3 วินาทีด้วยวิธีการด้านล่างสำหรับไฟล์ที่มีขนาดเกิน 20MB)ดังนั้นนี่คือสิ่งที่ฉันทำ:
sort file1 > file1.sorted
sort file2 > file2.sorted
diff file1.sorted file2.sorted | grep "<" | sed 's/^< *//' > files.diff
diff file1.sorted files.diff | grep "<" | sed 's/^< *//' > files.same.sorted
หากfiles.same.sorted
จะได้รับในคำสั่งเดียวกันกว่าที่เป็นต้นฉบับกว่าเพิ่มบรรทัดนี้สำหรับคำสั่งเดียวกันกว่า file1:
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same
หรือสำหรับคำสั่งเดียวกันมากกว่า file2:
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same
เพียงเพื่อการอ้างอิงหากมีใครบางคนยังคงมองหาวิธีการทำเช่นนี้กับหลาย ๆ ไฟล์ดูคำตอบที่เชื่อมโยงกับการหาเส้นที่ตรงกันในหลาย ๆ ไฟล์
เมื่อรวมสองคำตอบเหล่านี้ ( ans1และans2 ) ฉันคิดว่าคุณสามารถรับผลลัพธ์ที่ต้องการโดยไม่ต้องเรียงไฟล์:
#!/bin/bash
ans="matching_lines"
for file1 in *
do
for file2 in *
do
if [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then
echo "Comparing: $file1 $file2 ..." >> $ans
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
fi
done
done
เพียงบันทึกไว้ให้สิทธิ์ดำเนินการ ( chmod +x compareFiles.sh
) และเรียกใช้ จะใช้ไฟล์ทั้งหมดที่มีอยู่ในไดเรกทอรีการทำงานปัจจุบันและจะทำให้การเปรียบเทียบทั้งหมดเทียบกับทั้งหมดออกจากในไฟล์ "matching_lines"
สิ่งที่ต้องปรับปรุง:
rm file3.txt
cat file1.out | while read line1
do
cat file2.out | while read line2
do
if [[ $line1 == $line2 ]]; then
echo $line1 >>file3.out
fi
done
done
สิ่งนี้ควรทำ
rm -f file3.txt
ถ้าคุณจะลบไฟล์ ที่จะไม่รายงานข้อผิดพลาดใด ๆ หากไฟล์นั้นไม่มีอยู่ OTOH มันไม่จำเป็นถ้าสคริปต์ของคุณสะท้อนไปยังเอาต์พุตมาตรฐานปล่อยให้ผู้ใช้สคริปต์เลือกตำแหน่งที่เอาต์พุตควรไป ท้ายที่สุดคุณอาจต้องการใช้$1
และ$2
(อาร์กิวเมนต์บรรทัดคำสั่ง) แทนชื่อไฟล์คงที่ ( file1.out
และfile2.out
) นั่นทำให้อัลกอริทึม: มันจะช้า มันเป็นไปอ่านครั้งเดียวในแต่ละบรรทัดfile2.out
file1.out
มันจะช้าถ้าไฟล์มีขนาดใหญ่ (พูดหลายกิโลไบต์)
grep -F
ที่อ่านไฟล์หนึ่งไปยังหน่วยความจำแล้วส่งผ่านหนึ่งครั้งเพื่อหลีกเลี่ยงการวนซ้ำซ้ำ ๆ กันในไฟล์อินพุตทั้งสอง
comm
ต้องใช้ไฟล์อินพุตที่เรียงลำดับ หากคุณต้องการเพียงแค่บรรทัดต่อบรรทัดมันยอดเยี่ยม แต่ถ้าคุณต้องการสิ่งที่ฉันจะเรียกว่า "ต่อต้านแตกต่าง"comm
ไม่ทำงาน