Bash - จับคู่ไฟล์แต่ละบรรทัด


10

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

ตัวอย่าง

files.dat อ่านแบบนี้ในรูปแบบย่อตัวอักษรแต่ละตัวเป็นเส้นทางไฟล์ (สัมบูรณ์หรือสัมพัทธ์)

a
b
c
d
e

จากนั้นผลลัพธ์ของฉันควรมีลักษณะเช่นนี้:

a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

โดยเฉพาะอย่างยิ่งฉันต้องการที่จะแก้ปัญหานี้ในทุบตี แตกต่างจากคำถามอื่น ๆ รายการไฟล์ของฉันค่อนข้างเล็ก (ประมาณ 200 บรรทัด) ดังนั้นการใช้ลูปและความจุ RAM จึงไม่มีปัญหา


มันจะต้องอยู่ในทุบตีที่เหมาะสมหรือเพียงแค่บางสิ่งบางอย่างที่มีอยู่ผ่านทางบรรทัดคำสั่งทุบตี? ยูทิลิตี้อื่น ๆ อยู่ในตำแหน่งที่ดีกว่าในการประมวลผลข้อความ
Jeff Schaller

@JeffSchaller มีบางอย่างที่สามารถเข้าถึงได้ผ่าน bash commandline ฉันค่อนข้างไม่ชัดเจนขอโทษ
Enno

นี่เกือบจะกลายเป็นCode Golf : P
Richard de Wit

3
ตามกฎทั่วไปตราบใดที่คุณต้องทำสิ่งที่ไม่สำคัญให้ใช้ภาษาสคริปต์ที่คุณโปรดปรานผ่าน BASH มันจะบอบบางน้อยกว่า (เช่นเทียบกับอักขระพิเศษหรือช่องว่าง) และง่ายต่อการขยายเมื่อใดก็ตามที่คุณต้องการ (ถ้าคุณต้องการสามหรือกรองบางส่วนออกไป) Python หรือ Perl ควรติดตั้งในกล่อง Linux เกือบทุกกล่องจึงเป็นตัวเลือกที่ดี (เว้นแต่คุณกำลังทำงานกับระบบฝังตัวเช่น Busybox)
Davidmh

คำตอบ:


7

ใช้คำสั่งนี้:

awk '{ name[$1]++ }
    END { PROCINFO["sorted_in"] = "@ind_str_asc"
        for (v1 in name) for (v2 in name) if (v1 < v2) print v1, v2 }
        ' files.dat

PROCINFOอาจเป็นgawkส่วนขยาย หากคุณawkไม่สนับสนุนให้ปล่อยPROCINFO["sorted_in"] = "@ind_str_asc"บรรทัดและไพพ์เอาต์พุตลงในsort(ถ้าคุณต้องการเรียงลำดับเอาต์พุต)

(สิ่งนี้ไม่ต้องการให้มีการเรียงลำดับอินพุต)


8
$ join -j 2 -o 1.1,2.1 file file | awk '!seen[$1,$2]++ && !seen[$2,$1]++'
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

นี่ถือว่าไม่มีบรรทัดในไฟล์อินพุตที่มีช่องว่างใด ๆ มันจะถือว่าไฟล์นั้นถูกเรียงลำดับด้วย

joinคำสั่งสร้างสินค้าข้ามเต็มรูปแบบของเส้นในแฟ้ม ทำได้โดยการเข้าร่วมไฟล์กับตัวเองในฟิลด์ที่ไม่มีอยู่ ที่ไม่ได้มาตรฐาน-j 2อาจถูกแทนที่ด้วย-1 2 -2 2(แต่ไม่ได้-j2เว้นแต่คุณใช้ GNU join)

awkคำสั่งอ่านผลจากการนี้และเพียงเอาท์พุทผลลัพธ์ที่มีคู่ที่ยังไม่ได้รับการเห็น


คุณหมายถึงอะไรโดย "ไฟล์ถูกเรียงลำดับ"? จัดเรียงตามเกณฑ์ใด
Enno

@Enno เรียงวิธีที่sort -bจะเรียงมัน joinต้องใช้ไฟล์อินพุตที่เรียงลำดับ
Kusalananda

8

pythonวิธีการแก้ปัญหา ไฟล์อินพุตถูกป้อนไปยังitertools.combinationsจากไลบรารีมาตรฐานซึ่งสร้าง tuples 2 ความยาวที่จัดรูปแบบและพิมพ์ไปยังเอาต์พุตมาตรฐาน

python3 -c 'from itertools import combinations
with open("file") as f:
    lines = (line.rstrip() for line in f)
    lines = ("{} {}".format(x, y) for x, y in combinations(lines, 2))
    print(*lines, sep="\n")
'

6

หากคุณrubyติดตั้งแล้ว:

$ ruby -0777 -F'\n' -lane '$F.combination(2) { |c| puts c.join(" ")}' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
  • -0777 slurp ทั้งไฟล์ (ควรใช้ได้ตามที่กล่าวไว้ใน OP ว่าขนาดไฟล์เล็ก)
  • -F'\n'แยกตามบรรทัดใหม่ดังนั้นแต่ละบรรทัดจะเป็นองค์ประกอบใน$Fอาร์เรย์
  • $F.combination(2)สร้าง2องค์ประกอบการรวมกันในเวลา
  • { |c| puts c.join(" ")} พิมพ์ตามที่ต้องการ
  • หากไฟล์อินพุตสามารถมีรายการซ้ำให้ใช้ $F.uniq.combination(2)


สำหรับ 3 องค์ประกอบในเวลา:

$ ruby -0777 -F'\n' -lane '$F.combination(3) { |c| puts c.join(" ")}' ip.txt
a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e


ด้วยperl(ไม่ใช่แบบทั่วไป)

$ perl -0777 -F'\n' -lane 'for $i (0..$#F) {
                             for $j ($i+1..$#F) { 
                               print "$F[$i] $F[$j]\n" } }' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e


กับ awk

$ awk '{ a[NR]=$0 }
       END{ for(i=1;i<=NR;i++)
              for(j=i+1;j<=NR;j++)
                print a[i], a[j] }' ip.txt 
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

5

นี่คือหนึ่งในเปลือกบริสุทธิ์

test $# -gt 1 || exit
a=$1
shift
for f in "$@"
do
  echo $a $f
done
exec /bin/sh $0 "$@"

ตัวอย่าง:

~ (137) $ sh test.sh $(cat file.dat)
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
~ (138) $ 

1
การทดแทนคำสั่งตัดการขึ้นบรรทัดใหม่ดังนั้นคุณควรออกไปพร้อมกับสิ่งที่<file.dat xargs test.shดีกว่าtest.sh $(cat file.dat)
iruvar

1

ใช้Perlเราสามารถทำได้ตามที่แสดง:

$ perl -lne '
     push @A, $_}{
     while ( @A ) {
        my $e = shift @A;
        print "$e $_" for @A;
     }
' input.txt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.