sütununda bilge metin dosyalarını birleştirmek


52

İki tane metin dosyam var. İlki içeriğe sahip:

Languages
Recursively enumerable
Regular

ikincisinde içerik varken:

Minimal automaton
Turing machine
Finite

Onları sütun şeklinde bir dosyada birleştirmek istiyorum. Bu yüzden denedim paste 1 2ve çıktısı:

Languages   Minimal automaton
Recursively enumerable  Turing machine
Regular Finite

Bununla birlikte, sütunların bu gibi iyi hizalanmasını istiyorum.

Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

El ile işlem yapmadan bunu başarmanın mümkün olup olmadığını merak ediyordum.


Eklendi:

İşte Bruce yönteminin neredeyse çivilediği başka bir örnek, neden acaba yanlış hizalama?

$ 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)

3
Yanlış hizalama ile bu son örnek, bir sersem. Arch linux, pr (GNU coreutils) 8.12'ye kopyalayabilirim. Bunu, yaşlı bir Slackware'e (11.0) kopyalayamıyorum. Ayrıca, çevremde de var: pr (GNU coreutils) 5.97. Sorun '-' karakteriyle ve pr'de, yapıştırma ile değil.
Bruce Ediger

1
Aynı şeyi EM-DASH ile de aynı şekilde alıyorum prve expand... columnsbu sorunu önlüyorum.
Peter.O

Soldaki bir dosya herhangi bir sağdan daha kısa ise, en sağdaki sütun (lar) ın sola kaydırılacağı awk + paste dışındaki farklı cevapların çoğu için çıktılar ürettim . Aynısı ve dahası, sol sütunlarda boş satırlarla da bu sorunu yaşayan 'paste + column' için de geçerlidir ... Eğer tüm çıktıları birlikte görmek istiyorsanız. İşte link: paste.ubuntu.com/643692 4 sütun kullandım.
Peter.O

Ben sadece yanıltıcı bir şey fark paste.ubuntu Başlangıçta benim komut dosyaları, test etmek için veri ayarladı (ve bu diğerlerini yapmaya üzerinde led) ... yani demek alanları ... linke ➀ unicode may render oddly but the column count is ok kesinlikle yok değil uygulanır wc-paste-prve wc-paste-prdeğildir, onlar bizim sütun sayısı farklarını gösterme. Diğerleri tamam.
Peter.O

1
@BruceEdiger: En muhtemelen bozuk veya tarafından hiçbir işleme için ASCII olmayan karakterler kullanıldığında hizalama sorunu () karakteriyle - - yerine eksi (arasında) onun söz konusu OP bir tire (kullanılmış) oluşur prmultibyte arasında Geçerli yerel ayardaki karakterler (genellikle UTF8).
WhiteWinterWolf 13:16

Yanıtlar:


68

Sadece columnkomuta ihtiyacınız var ve ona sütunları ayırmak için sekmeler kullanmasını söyleyin.

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

"Boş hücre" tartışmasını ele almak için, şu -nseçeneğe ihtiyacımız var 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

Sütun adam sayfam -n"Debian GNU / Linux eklentisi" olduğunu gösteriyor. Fedora sistemim boş hücre sorunu göstermiyor: BSD'den türetilmiş görünüyor ve man sayfası "Sürüm 2.23 açgözlü olmak için -s seçeneğini değiştirdi" diyor


4
glenn: Sen saatin kahramanı sensin! Etrafta böyle bir şey olduğunu biliyordum ama hatırlayamadım. Bu soruyu gizliyordum; seni bekliyorum :) ... columnelbette; ne kadar açık (
tepede

4
Sadece column -s $'\t' -tboş hücreleri görmezden geldiğini fark ettim, sonraki tüm hücrelerin sağında (o satırda) sola hareket etmesine neden oldum; yani, bir dosyadaki boş bir satırın sonucu olarak veya daha kısa oluyor ... :(
Peter.O

1
@masi, düzeltildi
glenn jackman

-n RHEL'de çalışmıyor. Bir alternatif var mı?
Koshur

Sonunda yorum yapabilirim, bu nedenle daha önce Peter.O'nun boş hücrelerin boş bırakılmasıyla ilgili sorununu null kullanarak gideren bir cevap eklediğimi not etmek istiyorum.
tekno

11

Kullanışlı züppe prkomutunu arıyorsunuz :

paste file1 file2 | pr -t -e24

"-E24", "sekme duraklarını 24 boşluğa kadar genişlet" seçeneğidir. Neyse ki, pastesütunların arasına bir sekme karakteri koyar, böylece prgenişletebilir. "Recursively enumerable" içindeki karakterleri sayıp 24 ekleyerek 2'yi seçtim.


Teşekkürler! "Sekme duraklarını 24 boşluğa genişlet" ne anlama geliyor?
Tim

Ayrıca, yönteminizin hafif bir yanlış hizalama dışında neredeyse çivilendiği bir örnekle güncelleme yapıyorum.
Tim

Geleneksel olarak "sekme çubukları" her 8 alana çarpıyor. "123TABabc", satırın başından itibaren '8' karakter genişliğinde 'a' karakteriyle basılır. 24 değerine ayarlamak, 'a' sırasının başından itibaren 24 karakter genişliğine yerleştirir.
Bruce Ediger

Diyorsun ki "-e24" "sekmesi 24 mekanlara durur genişletmek" dir , yani neden kullanmaz expanddoğrudan komutu: paste file1 file2 | expand -t 24?
WhiteWinterWolf

1
@Masi - Cevabım benzer ama daha az karmaşık @ techno aşağıda cevap. Çağırmadı, sedbu yüzden çalışmayan bir işlem var. prUnix SysV günlerine dayanan eski bir komut olanı kullanıyor , sanırım, yani daha çok kurulumda var olabilir expand. Kısacası, sadece eski okul.
Bruce Ediger

9

Güncelleme : Burada, tablo çıktısı için daha basit bir komut dosyası (sorgunun sonundaki kod). Sadece olduğu gibi kendisine dosya geçmek paste... Bu kullanır htmlçerçeve yapmak, bu yüzden tweakable olduğunu. Birden çok alanı korur ve sütun hizalaması unicode karakterlerle karşılaştığında korunur. Ancak, editörün veya izleyicinin unicode'u yaratma şekli tamamen başka bir konudur ...

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

---

(Şimdiye kadar) cevaplarda sunulan araçların bir özeti .
Onlara oldukça yakından baktım; İşte ne buldum:

paste# Bu araç şu ana kadar sunulan tüm cevaplarda ortaktır. # Birden fazla dosyayı kullanabilir; bu nedenle birden fazla sütun ... İyi! # Her sütunu bir Sekme ile sınırlandırır ... Güzel. # Çıkışı tablo halinde değil.

Altındaki tüm aletler bu sınırlandırıcıyı kaldırır! ... Bir sınırlayıcıya ihtiyacınız varsa kötü.

column # Sekme sınırlayıcısını kaldırır, bu nedenle alan tanımlaması yalnızca oldukça iyi işlediği görünen sütunlarla yapılır. Kötü bir şey görmedim ... # Benzersiz bir sınırlayıcıya sahip olmam dışında, iyi çalışıyor!

expand # Yalnızca tek bir sekme ayarı vardır, bu nedenle 2 sütunun ötesinde tahmin edilemez. # Unicode kullanırken sütunların hizalanması doğru değildir ve Sekme sınırlayıcısını kaldırır, bu nedenle alan tanımlaması yalnızca sütun hizasına göredir

pr# Yalnızca tek bir sekme ayarı vardır, bu nedenle 2 sütunun ötesinde tahmin edilemez. # Unicode ile çalışırken sütunların hizalaması doğru değildir ve Sekme sınırlayıcıyı kaldırır, bu nedenle alan tanımlaması yalnızca sütun hizalaması ile yapılır

Bana göre, columntek bir astar olarak en iyi çözüm budur. İsterseniz sınırlayıcıyı ya da dosyalarınızın ASCII-sanat tasnifini istersiniz, okumaya devam edin, aksi halde .. columnsoldukça iyi.


Burada herhangi bir dosya numarası alan ve bir ASCII-sanat tablo halinde sunumu oluşturan bir betik vardır .. (Unicode'un beklenen genişliğe getiremeyebileceğini unutmayın, örn. A tek bir karakterdir. Bu sütuntan oldukça farklıdır. yukarıda belirtilen bazı yardımcı programlarda olduğu gibi, numaraların yanlış olması.) ... Komut dosyasının çıktısı, aşağıda gösterilen, F1 F2 F3 F4 ... adındaki 4 girdi dosyasındandır.

+------------------------+-------------------+-------------------+--------------+
| 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

İşte orjinal cevabım (yukarıdaki betiğin yerine biraz kırpılmış)

Kullanımı wcSütun genişliğini almak ve sedbir sağ yastığa görünür karakteri .(sadece bu örneğin) ... sonra pastebir ile iki sütun katılmak için Sekme 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

Sağ sütunu doldurmak istiyorsanız:

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...........

Teşekkürler! Çok fazla iş yaptın. Bu harika.
Tim

5

Neredeyse oradasın. pasteHer sütun arasına bir sekme karakteri koyar, bu nedenle tek yapmanız gereken sekmeleri genişletmektir. (Dosyalarınızın sekme içermediğini farz ediyorum.) Sol sütunun genişliğini belirlemeniz gerekiyor. (Yeterince yakın zamanda) GNU yardımcı programları wc -Lile en uzun satırın uzunluğunu gösterir. Diğer sistemlerde awk ile ilk geçişi yapın. +1Eğer sütunları arasında istediğiniz boş alan miktarıdır.

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

BSD sütun yardımcı programınız varsa, sütun genişliğini belirlemek ve bir seferde sekmeleri genişletmek için kullanabilirsiniz. ( değişmez bir sekme karakteridir; bash / ksh / zsh altında $'\t'yerine kullanabilirsiniz ve kullanabileceğiniz herhangi bir kabukta kullanabilirsiniz "$(printf '\t')".)

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

Benim versiyonunda wc: komut olması gerekiyor wc -L <left.txt... Bir dosya adı bir komut satırı olarak spedified zaman, çünkü arg , adı çıktı Stdout'a olduğunu
Peter.O

4

Bu çok adımlı, bu yüzden optimal değil, ama işte gidiyor.

1) içindeki en uzun çizginin uzunluğunu bulun file1.txt.

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

Örneğinizle, en uzun satır 22'dir.

2) file1.txtHer satırı, 22 karaktere kadar 22 karakterden daha küçük olanlarla doldurmak için awk tuşunu kullanın printf.

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

Not: FS için, içinde bulunmayan bir dize kullanın file1.txt.

3) Yapıştırmayı daha önce yaptığınız gibi kullanın.

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

Bu sık yaptığınız bir şeyse, kolayca bir betiğe dönüştürülebilir.


Kodunuzda en uzun satırı bulmak için ihtiyacınız var while IFS= read -r line, aksi takdirde kabuk boşlukları ve ters eğik çizgileri tutacaktır. Ancak kabuk bu iş için en iyi araç değildir; GNU coreutils'in son sürümlerinde (fred'in wc -Lcevabına bakınız) ya da awk: kullanabilirsiniz awk 'n<length {n=length} END {print +n}'.
Gilles 'SO- kötülük yapmayı bırak'

4

Glenn Jackson'ın cevabı hakkında yorum yapamıyorum, bu yüzden Peter.O'nun belirttiği boş hücrelerin sorununu ele almak için bunu ekliyorum. Her sekmeden önce boş bir karakter eklemek, tek bir ara olarak ele alınan sınırlayıcı işlemlerini ortadan kaldırır ve sorunu giderir. (Başlangıçta boşluk kullandım, ancak null karakterini kullanmak sütunlar arasındaki fazla boşluğu ortadan kaldırıyor.)

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

Boş karakter çeşitli nedenlerle sorunlara neden olursa, aşağıdakilerden birini deneyin:

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

veya

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

Her ikisi de sedve columnuygulamada Unix / Linux'un lezzetleri ve sürümleri, özellikle BSD (ve Mac OS X) ve GNU / Linux arasında değişiklik gösterdiği görülmektedir.


Bu sed komutu hiçbir şey yapmıyor gibi görünüyor. Column komutunu ile değiştiririm ve od -cboş bayt göremiyorum. Bu centos ve ubuntu'da.
glenn jackman

1
Bu benim için RedHat EL4'te çalıştı. Hem sed hem de sütun zamana ve sisteme göre değişebilir. Ubuntu'da 14.4 kullanmak baştan sona \0işe yaramadı nullama \x0yaptı. Ancak, daha sonra sütun bir line too longhata verdi . En basit şey bir boşluk kullanmak ve ekstra karakterle yaşamak gibi görünüyor.
techno

0

Bahamat'ın cevabı üzerine inşa etmek : bu tamamen yapılabilir awk, sadece bir kez dosyaları okuyabilir ve herhangi bir geçici dosya oluşturmaz. Problemi belirtildiği gibi çözmek,

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

Bu awkilkin birçok komutunda olduğu gibi, yukarıdakiler ilk önce okur file1, savedizideki tüm verileri kaydeder ve aynı anda maksimum satır uzunluğunu hesaplar. Ardından file2 , kayıtlı ( file1) verileri yan yana geçerli ( file2) verilerle okur ve yazdırır . Son olarak, eğer file1daha uzunsa file2(daha fazla satır varsa), son birkaç satırını file1 (bunun için ikinci sütunda karşılık gelen satır olmayan ) yazdırırız .

printfFormat ile ilgili olarak :

  • "%-nns"nngeniş bir alan karakterlerinde sola yaslanmış bir dize yazdırır .
  • "%-*s", nnaynı şeyi yapar - *alan genişliğini bir sonraki parametreden almasını söyler.
  • Kullanarak için , biz sütunlar arasındaki iki boşluk olsun. Açıkçası, ayarlanabilir.maxlength+2nn+2

Yukarıdaki komut dosyası yalnızca iki dosya için çalışır. Üç dosyayı işlemek ya da dört dosyayı işlemek için trivally değiştirilebilir, ancak bu sıkıcı olurdu ve bir egzersiz olarak bırakılır. Ancak, idare değiştirmek zor değil çıkıyor herhangi bir sayıda bir dosya:

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*

Bu benim ilk senaryomuza çok benzer

  • max_lengthBir diziye dönüşür .
  • max_FNRBir diziye dönüşür .
  • saveİki boyutlu bir diziye dönüşür .
  • Tüm dosyaları okur , tüm içeriği kaydeder . Sonra tüm çıktıyı ENDbloktan yazar.

Bu sorunun eski olduğunu biliyorum; Sadece tökezledim. Bunun pasteen iyi çözüm olduğuna katılıyorum ; özellikle, Glenn Jackson’ın paste file1 file2 | column -s $'\t' -t. Ancak awkyaklaşımı geliştirmeye çalışmanın eğlenceli olacağını düşündüm .
G-Man
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.