2013-03-02 24 views
10

Giả sử rằng tôi có hai tập tin, en.csvsp.csv, mỗi chứa chính xác hai kỷ lục bằng dấu phẩy:Làm thế nào để có được tất cả các lĩnh vực bên ngoài tham gia với Unix tham gia?

en.csv:

1,dog,red,car 
3,cat,white,boat 

sp.csv:

2,conejo,gris,tren 
3,gato,blanco,bote 

Nếu tôi thực hiện

join -t, -a 1 -a 2 -e MISSING en.csv sp.csv 

kết quả tôi nhận được là:

1,dog,red,car 
2,conejo,gris,tren 
3,cat,white,boat,gato,blanco,bote 

Lưu ý rằng tất cả các trường bị thiếu đều bị thu gọn. Để có được một "bên ngoài" đầy đủ tham gia, tôi cần phải xác định một định dạng; do đó

join -t, -a 1 -a 2 -e MISSING -o 0,1.2,1.3,1.4,2.2,2.3,2.4 en.csv sp.csv 

mang

1,dog,red,car,MISSING,MISSING,MISSING 
2,MISSING,MISSING,MISSING,conejo,gris,tren 
3,cat,white,boat,gato,blanco,bote 

Một nhược điểm của cách này để tạo ra một bên ngoài đầy đủ tham gia được rằng người ta cần phải xác định một cách rõ ràng các định dạng của bảng cuối cùng, mà có thể không được dễ dàng để làm trong chương trình các ứng dụng (trong đó danh tính của các bảng được nối kết chỉ được biết khi chạy).

Phiên bản gần đây của GNU join loại bỏ thiếu sót này bằng cách hỗ trợ định dạng đặc biệt auto. Do đó, với một phiên bản như vậy join lệnh cuối cùng trên có thể được thay thế bằng xa tổng quát hơn

join -t, -a 1 -a 2 -e MISSING -o auto en.csv sp.csv 

Làm thế nào tôi có thể đạt được hiệu ứng này tương tự với các phiên bản của join không hỗ trợ tùy chọn -o auto?


Bối cảnh và chi tiết

Tôi có một vỏ Unix (zsh) kịch bản được thiết kế để quá trình nhiều flatfiles CSV, và làm như vậy bằng cách làm cho rộng sử dụng GNU join 's' - o tự động 'tùy chọn. Tôi cần phải sửa đổi kịch bản này để nó có thể làm việc trong các môi trường nơi lệnh có sẵn join không hỗ trợ tùy chọn -o auto (như trường hợp cho BSD join cũng như cho các phiên bản cũ hơn của GNU join).

Một sử dụng điển hình của tùy chọn này trong kịch bản là một cái gì đó như:

_reccut() { 
    cols="1,$1" 
    shift 
    in=$1 
    shift 
    if (($# > 0)); then 
     join -t, -a 1 -a 2 -e 'MISSING' -o auto \ 
      <(cut -d, -f $cols $in | sort -t, -k1) \ 
      <(_reccut "[email protected]") 
    else 
     cut -d, -f $cols $in | sort -t, -k1 
    fi 
} 

Tôi thấy ví dụ này để minh họa rằng nó sẽ rất khó để thay thế -o auto với một định dạng rõ ràng, kể từ khi các lĩnh vực bao gồm trong này định dạng không được biết cho đến khi chạy.

Hàm _reccut ở trên cơ bản trích xuất các cột từ tệp và tham gia các bảng kết quả dọc theo cột đầu tiên của chúng.Để xem cách _reccut trong hành động, hãy tưởng tượng rằng, ngoài các tập tin đề cập ở trên, chúng tôi cũng có các tập tin

de.csv

2,Kaninchen,Grau,Zug 
1,Hund,Rot,Auto 

Sau đó, ví dụ, để hiển thị side-by-side cột 3 en.csv, cột 2 và 4 của sp.csv, và cột 3 của de.csv người ta sẽ chạy:

% _reccut 3 en.csv 2,4 sp.csv 3 de.csv | cut -d, 2- 
red,MISSING,MISSING,Rot 
MISSING,conejo,tren,Grau 
white,gato,bote,MISSING 
+1

đã phải làm chính xác những gì bạn đang nói về một dự án 1-off với Sun4, tôi nghĩ bạn đang bị mắc kẹt với mã hóa của riêng bạn, hoặc cung cấp GNU mới tham gia như là một phần của cài đặt của bạn. Xin lỗi, nhưng may mắn. – shellter

+1

Tôi đoán tôi nên thêm, sau khi rối tung nhiều xung quanh, tôi vết thương lên làm mảng assoc trong awk, với rắc rối ít hơn nhiều. Chúc may mắn. – shellter

Trả lời

1

đây là một giải pháp mà có thể hoặc không thể làm việc cho dữ liệu của bạn. Nó tiếp cận vấn đề bằng cách căn chỉnh các bản ghi bên trong tệp csv theo số dòng, tức là ghi 2 kết thúc trên dòng 2, ghi lại 3123 trên số dòng 3123 và cứ tiếp tục như vậy. Thiếu hồ sơ/dòng được đệm bằng MISSING lĩnh vực, vì vậy các tập tin đầu vào sẽ được đọc sai để trông như thế này:

en.csv:

1,dog,red,car 
2,MISSING,MISSING,MISSING 
3,cat,white,boat 

de.csv:

1,Hund,Rot,Auto 
2,Kaninchen,Grau,Zug 
3,MISSING,MISSING,MISSING 

sp.csv:

1,MISSING,MISSING,MISSING 
2,conejo,gris,tren 
3,gato,blanco,bote 

F rom ở đó rất dễ dàng để cắt ra các cột quan tâm và chỉ in chúng cạnh nhau bằng cách sử dụng paste.

Để đạt được điều này, chúng tôi sắp xếp các tập tin đầu vào đầu tiên và sau đó áp dụng một số ngu ngốc awk kỳ diệu:

  • Nếu một kỷ lục xuất hiện trên số dòng mong đợi của họ, hãy in nó
  • Nếu không, in càng nhiều các dòng chứa số lượng dự kiến ​​(điều này dựa trên số trường của dòng đầu tiên trong tệp, giống như những gì join -o auto làm) MISSING trường cho đến khi căn chỉnh chính xác trở lại
  • Không phải tất cả các tệp đầu vào đều sẽ cùng một số lượng hồ sơ, do đó, tối đa được tìm kiếm trước khi tất cả điều này. Sau đó, nhiều dòng hơn với các trường MISSING được in cho đến khi đạt đến mức tối đa.

reccut.sh:

#!/bin/bash 

get_max_recnum() 
{ 
    awk -F, '{ if ($1 > max) { max = $1 } } END { print max }' "[email protected]" 
} 

align_by_recnum() 
{ 
    sort -t, -k1 "$1" \ 
     | awk -F, -v MAXREC="$2" ' 
      NR==1 { for(x = 1; x < NF; x++) missing = missing ",MISSING" } 
      { 
       i = NR 
       if (NR < $1) 
       { 
        while (i < $1) 
        { 
         print i++ missing 
        } 
        NR+=i 
       } 
      }1 
      END { for(i++; i <= MAXREC; i++) { print i missing } } 
      ' 
} 

_reccut() 
{ 
    local infiles=() 
    local args=([email protected]) 
    for arg; do 
     infiles+=("$2") 
     shift 2 
    done 
    MAXREC="$(get_max_recnum "${infiles[@]}")" __reccut "${args[@]}" 
} 

__reccut() 
{ 
    local cols="$1" 
    local infile="$2" 
    shift 2 

    if (($# > 0)); then 
     paste -d, \ 
      <(align_by_recnum "${infile}" "${MAXREC}" | cut -d, -f ${cols}) \ 
      <(__reccut "[email protected]") 
    else 
     align_by_recnum "${infile}" "${MAXREC}" | cut -d, -f ${cols} 
    fi 
} 

_reccut "[email protected]" 

Run

$ ./reccut.sh 3 en.csv 2,4 sp.csv 3 de.csv 
red,MISSING,MISSING,Rot 
MISSING,conejo,tren,Grau 
white,gato,bote,MISSING 
+0

Sẽ thật tuyệt khi nhận được một số phản hồi về điều này, nó có hiệu quả với bạn không? –

Các vấn đề liên quan