ฉันแน่ใจว่าฉันเคยพบคำสั่ง 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ไม่ทำงาน