it-swarm-id.com

menggabungkan file teks dengan bijaksana

Saya punya dua file teks. Yang pertama memiliki konten:

Languages
Recursively enumerable
Regular

sedangkan yang kedua memiliki konten:

Minimal automaton
Turing machine
Finite

Saya ingin menggabungkan mereka ke dalam satu kolom file-bijaksana. Jadi saya mencoba paste 1 2 dan hasilnya adalah:

Languages   Minimal automaton
Recursively enumerable  Turing machine
Regular Finite

Namun saya ingin agar kolom disejajarkan dengan baik seperti

Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

Saya bertanya-tanya apakah mungkin untuk mencapai itu tanpa penanganan secara manual?


Ditambahkan:

Berikut adalah contoh lain, di mana metode Bruce hampir berhasil, kecuali beberapa ketidaksejajaran tentang yang saya bertanya-tanya mengapa?

$ 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

Anda hanya perlu perintah column , dan katakan untuk menggunakan tab untuk memisahkan kolom

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

Untuk mengatasi kontroversi "sel kosong", kita hanya perlu -n pilihan ke 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

Halaman manual kolom saya menunjukkan -n adalah "ekstensi Debian GNU/Linux." Sistem Fedora saya tidak menunjukkan masalah sel kosong: tampaknya berasal dari BSD dan halaman manual mengatakan "Versi 2.23 mengubah opsi -s menjadi non-serakah"

71
glenn jackman

Anda sedang mencari perintah dandy pr yang berguna:

paste file1 file2 | pr -t -e24

"-E24" adalah "memperluas tab berhenti ke 24 spasi". Untungnya, paste menempatkan karakter tab di antara kolom, sehingga pr dapat memperluasnya. Saya memilih 24 dengan menghitung karakter dalam "Recursively enumerable" dan menambahkan 2.

12
Bruce Ediger

Pembaruan: Ini dia skrip yang lebih sederhana (yang ada di akhir pertanyaan) untuk hasil tabulasi. Cukup berikan nama file seperti yang Anda inginkan ke paste... Ini menggunakan html untuk membuat bingkai, jadi itu dapat diubah. Itu memang melestarikan banyak ruang, dan penyelarasan kolom dipertahankan ketika bertemu karakter unicode. Namun, cara editor atau pemirsa merender unicode adalah masalah lain sepenuhnya ...

┌──────────────────────┬────────────────┬──────────┬────────────────────────────┐
│ 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'

---

Sinopsis alat disajikan dalam jawaban (sejauh ini).
Saya sudah cukup dekat melihat mereka; inilah yang saya temukan:

paste # Alat ini umum untuk semua jawaban yang disajikan sejauh ini # Dapat menangani banyak file; karena itu banyak kolom ... Bagus! # Ini membatasi setiap kolom dengan Tab ... Bagus. # Outputnya tidak ditabulasi.

Semua alat di bawah ini menghapus pembatas ini! ... Buruk jika Anda membutuhkan pembatas.

column # Ini menghapus pembatas Tab, jadi pengidentifikasi bidang murni oleh kolom yang tampaknya menangani dengan sangat baik .. Saya belum melihat sesuatu yang salah ... # Selain tidak memiliki pembatas yang unik, ia berfungsi baik!

expand # Hanya memiliki pengaturan tab tunggal, sehingga tidak dapat diprediksi melebihi 2 kolom # Penjajaran kolom tidak akurat saat menangani unicode, dan menghapus pembatas Tab, jadi identifikasi bidang murni oleh perataan kolom

pr # Hanya memiliki pengaturan satu tab, sehingga tidak dapat diprediksi melebihi 2 kolom. # Penjajaran kolom tidak akurat saat menangani unicode, dan menghapus pembatas Tab, jadi identifikasi bidang murni oleh perataan kolom

Bagi saya, column ini solusi terbaik yang jelas sebagai satu-baris .. Anda ingin pembatas, atau tabluasi ASCII-art dari file Anda, baca terus, jika tidak .. columns sangat bagus :).


Berikut ini adalah skrip yang mengambil numper file apa pun dan membuat presentasi tabulasi ASCII-art .. (Ingatlah bahwa unicode mungkin tidak merender sesuai lebar yang diharapkan, mis. ௵ yang merupakan karakter tunggal. Ini sangat berbeda dengan kolom nomor menjadi salah, seperti halnya di beberapa utilitas yang disebutkan di atas.) ... Keluaran skrip, yang ditunjukkan di bawah ini, berasal dari 4 file input, bernama 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

Inilah jawaban asli saya (sedikit dipangkas sebagai pengganti skrip di atas)

Menggunakan wc untuk mendapatkan lebar kolom, dan sed ke kanan dengan pad terlihat karakter . (hanya untuk contoh ini) ... lalu paste untuk bergabung dengan dua kolom dengan Tab karakter ...

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

Jika Anda ingin mengisi kolom kanan:

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

Kamu hampir sampai. paste menempatkan karakter tab di antara setiap kolom, jadi yang perlu Anda lakukan adalah memperluas tab. (Saya menganggap file Anda tidak mengandung tab.) Anda perlu menentukan lebar kolom kiri. Dengan (cukup baru) GNU utilitas, wc -L Menunjukkan panjang dari garis terpanjang. Pada sistem lain, buat pass pertama dengan awk. +1 Adalah jumlah ruang kosong yang Anda inginkan di antara kolom.

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}')

Jika Anda memiliki utilitas kolom BSD, Anda dapat menggunakannya untuk menentukan lebar kolom dan memperluas tab sekaligus. ( Adalah karakter tab literal; di bawah bash/ksh/zsh Anda dapat menggunakan $'\t' Sebagai gantinya, dan dalam Shell apa pun Anda dapat menggunakan "$(printf '\t')".)

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

Ini multi-langkah, jadi ini tidak optimal, tapi begini saja.

1) Temukan panjang garis terpanjang di file1.txt.

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

Dengan contoh Anda, garis terpanjang adalah 22.

2) Gunakan awk untuk menulis file1.txt, melapisi setiap baris kurang dari 22 karakter hingga 22 dengan pernyataan printf.

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

Catatan: Untuk FS, gunakan string yang tidak ada di file1.txt.

3) Gunakan tempel seperti yang Anda lakukan sebelumnya.

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

Jika ini adalah sesuatu yang sering Anda lakukan, ini dapat dengan mudah diubah menjadi skrip.

4
bahamat

Saya tidak dapat mengomentari jawaban glenn jackman, jadi saya menambahkan ini untuk mengatasi masalah sel kosong yang dicatat Peter.O. Menambahkan null char sebelum setiap tab menghilangkan proses pembatas yang diperlakukan sebagai satu break dan mengatasi masalah tersebut. (Saya awalnya menggunakan spasi, tetapi menggunakan null char menghilangkan ruang ekstra di antara kolom.)

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

Jika null char menyebabkan masalah karena berbagai alasan, cobalah:

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

atau

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

Baik sed dan column tampaknya bervariasi dalam implementasi lintas rasa dan versi Unix/Linux, terutama BSD (dan Mac OS X) vs. GNU/Linux.

4
techno

Membangun jawaban bahamat : ini dapat dilakukan sepenuhnya dalam awk, membaca file hanya sekali dan tidak membuat file sementara. Untuk mengatasi masalah seperti yang dinyatakan, lakukan

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

Seperti banyak skrip awk dari sejenis ini, yang pertama di atas berbunyi file1, menyimpan semua data dalam array save dan secara bersamaan menghitung panjang garis maksimum. Kemudian berbunyi file2 dan mencetak yang disimpan (file1) data berdampingan dengan arus (file2) data. Akhirnya, jika file1 lebih panjang dari file2 (memiliki lebih banyak baris), kami mencetak beberapa baris terakhir file1 (yang tidak ada baris yang sesuai di kolom kedua).

Mengenai format printf:

  • "%-nns" mencetak string yang dibenarkan kiri dalam bidang nn lebar karakter.
  • "%-*s", nn melakukan hal yang sama - * memintanya untuk mengambil lebar bidang dari parameter berikutnya.
  • Dengan menggunakan maxlength+2 untuk nn, kami mendapatkan dua spasi di antara kolom. Jelas +2 dapat disesuaikan.

Script di atas hanya berfungsi untuk dua file. Itu dapat dengan sepele dimodifikasi untuk menangani tiga file, atau untuk menangani empat file, dll, tetapi ini akan membosankan dan dibiarkan sebagai latihan. Namun, ternyata tidak sulit untuk memodifikasinya untuk ditangani nomor apa saja dari file:

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*

Ini sangat mirip dengan skrip pertama saya, kecuali

  • Itu berputar max_length ke dalam array.
  • Itu berputar max_FNR ke dalam array.
  • Itu mengubah save menjadi array dua dimensi.
  • Bunyinya semua file, menyimpan semua konten. Kemudian ia menulis semua output dari blok END.