it-swarm-vi.com

kết hợp các tập tin văn bản cột khôn ngoan

Tôi có hai tập tin văn bản. Cái đầu tiên có nội dung:

Languages
Recursively enumerable
Regular

trong khi cái thứ hai có nội dung:

Minimal automaton
Turing machine
Finite

Tôi muốn kết hợp chúng thành một cột tập tin. Vì vậy, tôi đã cố gắng paste 1 2 và đầu ra của nó là:

Languages   Minimal automaton
Recursively enumerable  Turing machine
Regular Finite

Tuy nhiên tôi muốn có các cột được căn chỉnh tốt như

Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

Tôi đã tự hỏi nếu nó có thể đạt được điều đó mà không cần xử lý bằng tay?


Thêm:

Đây là một ví dụ khác, trong đó phương pháp Bruce gần như đóng đinh nó, ngoại trừ một số sai lệch nhỏ về điều mà tôi tự hỏi tại sao?

$ cat 1
Chomsky hierarchy
Type-0
—

$ cat 2
Grammars
Unrestricted

$ paste 1 2 | pr -t -e20
Chomsky hierarchy   Grammars
Type-0              Unrestricted
—                    (no common name)
54
Tim

Bạn chỉ cần lệnh column và bảo nó sử dụng các tab để tách các cột

paste file1 file2 | column -s $'\t' -t

Để giải quyết tranh cãi "ô trống", chúng ta chỉ cần -n tùy chọn để column:

$ paste <(echo foo; echo; echo barbarbar) <(seq 3) | column -s $'\t' -t
foo        1
2
barbarbar  3

$ paste <(echo foo; echo; echo barbarbar) <(seq 3) | column -s $'\t' -tn
foo        1
           2
barbarbar  3

Trang man cột của tôi chỉ ra -n là một "phần mở rộng Debian GNU/Linux." Hệ thống Fedora của tôi không thể hiện vấn đề ô trống: có vẻ như nó bắt nguồn từ BSD và trang man nói "Phiên bản 2.23 đã thay đổi tùy chọn -s thành không tham lam"

71
glenn jackman

Bạn đang tìm kiếm lệnh dandy pr tiện dụng:

paste file1 file2 | pr -t -e24

"-E24" là "tab mở rộng dừng thành 24 khoảng trắng". May mắn thay, paste đặt một ký tự tab giữa các cột, vì vậy pr có thể mở rộng nó. Tôi đã chọn 24 bằng cách đếm các ký tự trong "Số đệ quy" và thêm 2.

12
Bruce Ediger

Cập nhật: Đây là một tập lệnh đơn giản hơn nhiều (tập lệnh ở cuối câu hỏi) cho đầu ra được lập bảng. Chỉ cần truyền tên tệp cho nó như bạn muốn paste... Nó sử dụng html để tạo khung, vì vậy nó có thể điều chỉnh được. Nó bảo tồn nhiều khoảng trắng và căn chỉnh cột được giữ nguyên khi gặp các ký tự unicode. Tuy nhiên, cách trình soạn thảo hoặc trình xem kết xuất unicode lại là một vấn đề khác hoàn toàn ...

┌──────────────────────┬────────────────┬──────────┬────────────────────────────┐
│ Languages            │ Minimal        │ Chomsky  │ Unrestricted               │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ Recursive            │ Turing machine │ Finite   │     space indented         │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ Regular              │ Grammars       │          │ ➀ unicode may render oddly │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ 1 2  3   4    spaces │                │ Symbol-& │ but the column count is ok │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│                      │                │          │ Context                    │
└──────────────────────┴────────────────┴──────────┴────────────────────────────┘

#!/bin/bash
{ echo -e "<html>\n<table border=1 cellpadding=0 cellspacing=0>"
  paste "[email protected]" |sed -re 's#(.*)#\x09\1\x09#' -e 's#\x09# </pre></td>\n<td><pre> #g' -e 's#^ </pre></td>#<tr>#' -e 's#\n<td><pre> $#\n</tr>#'
  echo -e "</table>\n</html>"
} |w3m -dump -T 'text/html'

---

Bản tóm tắt của các công cụ được trình bày trong các câu trả lời (cho đến nay).
[.__.] Tôi đã có một cái nhìn khá gần với họ; đây là những gì tôi đã tìm thấy:

paste # Công cụ này phổ biến cho tất cả các câu trả lời được trình bày cho đến nay # Nó có thể xử lý nhiều tệp; do đó nhiều cột ... Tốt! # Nó phân định từng cột bằng một Tab ... Tốt. # Đầu ra của nó không được lập bảng.

Tất cả các công cụ dưới đây đều loại bỏ dấu phân cách này! ... Xấu nếu bạn cần một dấu phân cách.

column # Nó loại bỏ dấu phân cách Tab, do đó, nhận dạng trường hoàn toàn bằng các cột mà nó dường như xử lý khá tốt .. Tôi không phát hiện ra bất cứ điều gì tồi tệ ... # Ngoài việc không có một dấu phân cách duy nhất, nó hoạt động khỏe!

expand # Chỉ có một cài đặt tab duy nhất, do đó không thể đoán trước ngoài 2 cột # Việc căn chỉnh các cột không chính xác khi xử lý unicode và nó loại bỏ dấu phân cách Tab, do đó, nhận dạng trường hoàn toàn là do căn chỉnh cột

pr # Chỉ có một cài đặt tab duy nhất, do đó không thể đoán trước ngoài 2 cột. # Việc căn chỉnh các cột không chính xác khi xử lý unicode và nó loại bỏ dấu phân cách Tab, do đó, nhận dạng trường hoàn toàn bằng cách căn chỉnh cột

Đối với tôi, column đó là soluton rõ ràng nhất dưới dạng một lớp lót .. Bạn muốn hoặc là dấu phân cách nghệ thuật hoặc bảng mã nghệ thuật ASCII của các tệp của bạn, đọc tiếp, nếu không .. columns là khá tốt :) ...


Đây là một tập lệnh lấy bất kỳ số tập tin nào và tạo một bản trình bày được lập bảng theo nghệ thuật ASCII .. (Hãy nhớ rằng unicode có thể không hiển thị theo chiều rộng dự kiến, ví dụ: ௵ là một ký tự đơn. Điều này khá khác với cột các số bị sai, như trường hợp trong một số tiện ích được đề cập ở trên.) ... Đầu ra của tập lệnh, được hiển thị bên dưới, là từ 4 tệp đầu vào, được đặt tên là F1 F2 F3 F4 ...

+------------------------+-------------------+-------------------+--------------+
| Languages              | Minimal automaton | Chomsky hierarchy | Grammars     |
| Recursively enumerable | Turing machine    | Type-0            | Unrestricted |
| Regular                | Finite            | —                 |              |
| Alphabet               |                   | Symbol            |              |
|                        |                   |                   | Context      |
+------------------------+-------------------+-------------------+--------------+

#!/bin/bash

# Note: The next line is for testing purposes only!
set F1 F2 F3 F4 # Simulate commandline filename args $1 $2 etc...

p=' '                                # The pad character
# Get line and column stats
cc=${#@}; lmax=                      # Count of columns (== input files)
for c in $(seq 1 $cc) ;do            # Filenames from the commandline 
  F[$c]="${!c}"        
  wc=($(wc -l -L <${F[$c]}))         # File length and width of longest line 
  l[$c]=${wc[0]}                     # File length  (per file)
  L[$c]=${wc[1]}                     # Longest line (per file) 
  ((lmax<${l[$c]})) && lmax=${l[$c]} # Length of longest file
done
# Determine line-count deficits  of shorter files
for c in $(seq 1 $cc) ;do  
  ((${l[$c]}<lmax)) && D[$c]=$((lmax-${l[$c]})) || D[$c]=0 
done
# Build '\n' strings to cater for short-file deficits
for c in $(seq 1 $cc) ;do
  for n in $(seq 1 ${D[$c]}) ;do
    N[$c]=${N[$c]}$'\n'
  done
done
# Build the command to suit the number of input files
source=$(mktemp)
>"$source" echo 'paste \'
for c in $(seq 1 $cc) ;do
    ((${L[$c]}==0)) && e="x" || e=":a -e \"s/^.{0,$((${L[$c]}-1))}$/&$p/;ta\""
    >>"$source" echo '<(sed -re '"$e"' <(cat "${F['$c']}"; echo -n "${N['$c']}")) \'
done
# include the ASCII-art Table framework
>>"$source" echo ' | sed  -e "s/.*/| & |/" -e "s/\t/ | /g" \'   # Add vertical frame lines
>>"$source" echo ' | sed -re "1 {h;s/[^|]/-/g;s/\|/+/g;p;g}" \' # Add top and botom frame lines 
>>"$source" echo '        -e "$ {p;s/[^|]/-/g;s/\|/+/g}"'
>>"$source" echo  
# Run the code
source "$source"
rm     "$source"
exit

Đây là câu trả lời ban đầu của tôi (cắt bớt một chút thay cho kịch bản trên)

Sử dụng wc để lấy chiều rộng cột và sed sang bên phải với một hiển thị ký tự . (chỉ cho ví dụ này) ... và sau đó paste để nối hai cột với một Tab char ...

paste <(sed -re :a -e 's/^.{1,'"$(($(wc -L <F1)-1))"'}$/&./;ta' F1) F2

# output (No trailing whitespace)
Languages.............  Minimal automaton
Recursively enumerable  Turing machine
Regular...............  Finite

Nếu bạn muốn điền vào cột bên phải:

paste <( sed -re :a -e 's/^.{1,'"$(($(wc -L <F1)-1))"'}$/&./;ta' F1 ) \
      <( sed -re :a -e 's/^.{1,'"$(($(wc -L <F2)-1))"'}$/&./;ta' F2 )  

# output (With trailing whitespace)
Languages.............  Minimal automaton
Recursively enumerable  Turing machine...
Regular...............  Finite...........
9
Peter.O

Bạn đã gần tới. paste đặt một ký tự tab giữa mỗi cột, vì vậy tất cả những gì bạn cần làm là mở rộng các tab. (Tôi cho rằng các tệp của bạn không chứa các tab.) Bạn cần xác định độ rộng của cột bên trái. Với (đủ gần đây) GNU tiện ích, wc -L Hiển thị độ dài của dòng dài nhất. Trên các hệ thống khác, hãy thực hiện lần đầu tiên với awk. +1 Là số lượng không gian trống bạn muốn giữa các cột.

paste left.txt right.txt | expand -t $(($(wc -L <left.txt) + 1))
paste left.txt right.txt | expand -t $(awk 'n<length {n=length} END {print n+1}')

Nếu bạn có tiện ích cột BSD, bạn có thể sử dụng nó để xác định độ rộng cột và mở rộng các tab trong một lần. ( Là ký tự tab theo nghĩa đen; trong bash/ksh/zsh, bạn có thể sử dụng $'\t', Và trong mọi Shell bạn có thể sử dụng "$(printf '\t')".)

paste left.txt right.txt | column -s '␉' -t

Đây là nhiều bước, vì vậy nó không tối ưu, nhưng ở đây đi.

1) Tìm độ dài của dòng dài nhất trong file1.txt.

while read line
do
echo ${#line}
done < file1.txt | sort -n | tail -1

Với ví dụ của bạn, dòng dài nhất là 22.

2) Sử dụng awk để đệm file1.txt, đệm mỗi dòng nhỏ hơn 22 ký tự lên đến 22 bằng câu lệnh printf.

awk 'FS="---" {printf "%-22s\n", $1}' < file1.txt > file1-pad.txt

Lưu ý: Đối với FS, sử dụng chuỗi không tồn tại trong file1.txt.

3) Sử dụng dán như bạn đã làm trước đây.

$ paste file1-pad.txt file2.txt
Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

Nếu đây là việc bạn làm thường xuyên, điều này có thể dễ dàng biến thành kịch bản.

4
bahamat

Tôi không thể nhận xét về câu trả lời của glenn jackman, vì vậy tôi đang thêm điều này để giải quyết vấn đề về các ô trống mà Peter.O lưu ý. Thêm một char char trước mỗi tab sẽ giúp loại bỏ các dấu phân cách được coi là một dấu ngắt đơn và giải quyết vấn đề. (Ban đầu tôi sử dụng khoảng trắng, nhưng sử dụng null char sẽ loại bỏ khoảng trắng thừa giữa các cột.)

paste file1 file2 | sed 's/\t/\0\t/g' | column -s $'\t' -t

Nếu null char gây ra sự cố vì nhiều lý do, hãy thử:

paste file1 file2 | sed 's/\t/ \t/g' | column -s $'\t' -t

hoặc là

paste file1 file2 | sed $'s/\t/ \t/g' | column -s $'\t' -t

Cả sedcolumn dường như khác nhau về cách triển khai giữa các hương vị và phiên bản của Unix/Linux, đặc biệt là BSD (và Mac OS X) so với GNU/Linux.

4
techno

Dựa trên bahamat từ trả lời : điều này có thể được thực hiện hoàn toàn trong awk, chỉ đọc các tệp một lần và không tạo bất kỳ tệp tạm thời nào. Để giải quyết vấn đề như đã nêu, hãy làm

awk '
        NR==FNR { if (length > max_length) max_length = length
                  max_FNR = FNR
                  save[FNR] = $0
                  next
                }
                { printf "%-*s", max_length+2, save[FNR]
                  print
                }
        END     { if (FNR < max_FNR) {
                        for (i=FNR+1; i <= max_FNR; i++) print save[i]
                  }
                }
    '   file1 file2

Cũng như nhiều tập lệnh awk của ilk này, lần đầu tiên ở trên đọc file1, lưu tất cả dữ liệu trong mảng save và đồng thời tính toán độ dài dòng tối đa. Sau đó, nó đọc file2 và in lưu (file1) dữ liệu song song với hiện tại (file2) dữ liệu. Cuối cùng, nếu file1 dài hơn file2 (có nhiều dòng hơn), chúng tôi in vài dòng cuối cùng của file1 (những cái không có dòng tương ứng trong cột thứ hai).

Về định dạng printf:

  • "%-nns" in một chuỗi được căn trái trong trường nn rộng ký tự.
  • "%-*s", nn làm điều tương tự - * bảo nó lấy độ rộng trường từ tham số tiếp theo.
  • Bằng cách sử dụng maxlength+2 for nn, chúng tôi nhận được hai khoảng trắng giữa các cột. Rõ ràng là +2 có thể được điều chỉnh.

Kịch bản trên chỉ hoạt động cho hai tập tin. Nó có thể được sửa đổi một cách tầm thường để xử lý ba tệp hoặc để xử lý bốn tệp, v.v., nhưng điều này sẽ rất tẻ nhạt và bị bỏ lại như một bài tập. Tuy nhiên, hóa ra không khó để sửa đổi nó để xử lý bất kỳ số nào of tệp:

awk '
        FNR==1  { file_num++ }
                { if (length > max_length[file_num]) max_length[file_num] = length
                  max_FNR[file_num] = FNR
                  save[file_num,FNR] = $0
                }
        END     { for (j=1; j<=file_num; j++) {
                        if (max_FNR[j] > global_max_FNR) global_max_FNR = max_FNR[j]
                  }
                  for (i=1; i<=global_max_FNR; i++) {
                        for (j=1; j<file_num; j++) printf "%-*s", max_length[j]+2, save[j,i]
                        print save[file_num,i]
                  }
                }
    '   file*

Điều này rất giống với kịch bản đầu tiên của tôi, ngoại trừ

  • Nó rẽ max_length thành một mảng.
  • Nó rẽ max_FNR thành một mảng.
  • Nó biến save thành một mảng hai chiều.
  • Nó đọc tất cả các tệp, lưu tất cả nội dung. Sau đó, nó viết ra all đầu ra từ khối END.