テキストファイルを列ごとに結合する


52

2つのテキストファイルがあります。最初のものにはコンテンツがあります:

Languages
Recursively enumerable
Regular

2番目のものにはコンテンツがあります:

Minimal automaton
Turing machine
Finite

それらを列ごとに1つのファイルに結合したいと思います。だから私は試しましたがpaste 1 2、その出力は次のとおりです。

Languages   Minimal automaton
Recursively enumerable  Turing machine
Regular Finite

しかし、私は次のように列をうまく整列させたい

Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

手動で処理せずにそれを達成できるかどうか疑問に思っていましたか?


追加:

ここに別の例がありますが、ブルースの方法はそれをほとんど釘付けにしますが、わずかなミスアライメントを除いて、なぜだろうか?

$ 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
ずれを伴う最後の例は、すごいものです。Arch Linux、pr(GNU coreutils)8.12で複製できます。私が持っている高齢のSlackware(11.0)でそれを複製することはできません:pr(GNU coreutils)5.97。問題は「-」文字にあり、貼り付けではなくprにあります。
ブルースエディガー

1
私は両方を持つEM-DASHと同じものを取得prし、expand... columnsこの問題を回避することができます。
Peter.O

awk + ​​pasteを除き、ほとんどの異なる回答の出力を生成しました。これは、左のファイルがその右の任意のtより短い場合、右端の列を左にシフトします。同じことは、「貼り付け+列」にも当てはまります。左列に空白行があるという問題もあります。すべての出力を一緒に表示したい場合。ここにリンクがあります:paste.ubuntu.com/643692 4列を使用しました。
Peter.O

私に誤解を招くだけ注目何かpaste.ubuntuの言うフィールドので、リンク...私はもともと、私のスクリプトをテストするためのデータを設定(およびそれが他の人をやってするにつながった)... ➀ unicode may render oddly but the column count is ok 間違いがないではないに適用するwc-paste-prと、wc-paste-pr彼ら列数の違いを表示します。他は大丈夫です。
Peter.O

1
@BruceEdiger:非ASCII文字が(OPダッシュを(使用し、彼の質問に)文字- - (代わりにマイナスの))が使用されている場合、アライメントの問題が発生した不良またはにより、無処理のために、おそらく、prマルチバイトの現在のロケールの文字(通常UTF8)。
ホワイトウィンターウルフ

回答:


68

columnコマンドが必要なだけで、タブを使用して列を区切るように指示します

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

「空のセル」論争に対処するには、次の-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

私のコラムのマニュアルページ-nは、「Debian GNU / Linux拡張機能」であることを示しています。私のFedoraシステムは空のセルの問題を示していません:BSDから派生したようで、manページには「バージョン2.23が-sオプションを貪欲でないように変更しました」とあります


4
グレン:あなたはその時のヒーローです!このようなものがあることは知っていましたが、思い出せませんでした。私はこの質問に潜んでいます。あなたを待っている :) ... column、もちろん。どのように明らかに(後知恵で)+1 ...おかげで...
Peter.O

4
column -s $'\t' -t空のセルを無視することに気づきました。その結果、その右側の(その行で)後続のすべてのセルが左に移動します。すなわち、ファイル内の空白行の結果として、またはそれが短くなっています... :(
Peter.O

1
@masi、訂正
グレンジャックマン

-nはRHELでは機能しません。代替手段はありますか?
コシュール

最後にコメントできますので、以前に空を使用した空のセルの実行に関するPeter.Oの問題に対処する回答を以下に追加したことに注意してください。
テクノ

11

あなたは便利なダンディprコマンドを探しています:

paste file1 file2 | pr -t -e24

「-e24」は「タブストップを24スペースに拡張する」です。幸いなことに、paste列の間にタブ文字を挿入するので、それprを拡張できます。「再帰列挙可能」の文字をカウントし、2を追加して24を選択しました。


ありがとう!「タブストップを24スペースに拡張する」とはどういう意味ですか?
ティム

また、わずかなミスアライメントを除いて、メソッドがほぼ釘付けになる例を更新します。
ティム

伝統的に「タブストップ」は8スペースごとにヒットします。「123TABabc」は、行の先頭から8文字幅の「a」文字で出力されます。24に設定すると、行の先頭から24文字の幅で「a」が配置されます。
ブルースエディガー

「-e24」は「タブストップを24個のスペースに展開する」と言うので、expandコマンドを直接使用しないのはなぜpaste file1 file2 | expand -t 24ですか。
ホワイトウィンターウルフ

1
@Masi-私の答えは似ていますが、以下の@technoの答えほど複雑ではありません。起動しないためsed、実行されないプロセスが1つあります。prUnix SysV時代にさかのぼる古代のコマンドを使用しているため、より多くのインストールで存在する可能性がありますexpand。要するに、古い学校です。
ブルースエディガー

9

更新:ここでは、表形式の出力用のはるかに単純なスクリプト(質問の最後にあるスクリプト)を示します。あなたがするようにファイル名を渡すだけpasteです...それはhtmlフレームを作るために使用するので、調整可能です。複数のスペースが保持され、Unicode文字が検出されたときに列の配置が保持されます。ただし、エディターまたはビューアーレンダラーがUnicodeをレンダリングする方法は、まったく別の問題です...

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

---

回答に示されているツールの概要(これまで)。
私はそれらをかなりよく見てきました。ここに私が見つけたものがあります:

paste#このツールは、これまでに提示されたすべての回答に共通です#複数のファイルを処理できます。したがって、複数の列...いいね!#各列をタブで区切る...良い。#その出力は集計されません。

以下のすべてのツールはすべて、この区切り文字を削除します!...区切り文字が必要な場合は悪いです。

column #それはタブ区切り文字を削除するので、フィールドの識別は純粋に列によって行われます。これは非常にうまく処理できるようです。.間違ったものは見当たりませんでした。

expand #タブ設定は1つのみであるため、2列を超えると予測できません#Unicodeを処理する場合、列の配置は正確ではなく、タブ区切り文字が削除されるため、フィールドの識別は純粋に列の配置によって行われます

pr#タブ設定は1つだけなので、2列を超えると予測できません。#ユニコードを処理する場合、列の配置は正確ではなく、タブ区切り文字が削除されるため、フィールドの識別は純粋に列の配置によって行われます

私にとって、columnそれは、ワンライナーとしての明らかな最高のソルトンです..区切り文字、またはファイルのASCIIアートのタブレーションのいずれかが必要です。そうでなければ読み進めてくださいcolumns


これは、任意の数のファイルを取得し、ASCIIアートの表形式プレゼンテーションを作成するスクリプトです。上記のユーティリティの一部の場合のように、数字が間違っています。)...以下に示すスクリプトの出力は、F1 F2 F3 F4という名前の4つの入力ファイルからのものです。

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

ここに私の元の答えがあります(上記のスクリプトの代わりに少しトリミングされています)

を使用wcして列の幅を取得sedし、表示されている文字で右パッドします.(この例の場合のみ)...そしてpaste、2つの列をTab文字で結合します...

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

正しい列を埋めたい場合:

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

ありがとう!あなたは非常に多くの仕事をしました。すごい。
ティム

5

あなたはほとんどそこにいます。paste各列の間にタブ文字を挿入するため、必要なことはタブを展開することだけです。(ファイルにタブが含まれていないことを前提としています。)左の列の幅を決定する必要があります。(最新の)GNUユーティリティwc -Lを使用すると、最も長い行の長さが表示されます。他のシステムでは、awkで最初のパスを作成します。これ+1は、列間に必要な空白の量です。

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列ユーティリティがある場合は、それを使用して列幅を決定し、タブを一度に展開できます。(リテラルのタブ文字です。bash/ ksh / zsh $'\t'では代わりに使用でき、シェルでは"$(printf '\t')"。を使用できます。)

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

私のバージョンではwc:、コマンドは次のようにする必要がありますwc -L <left.txt...ファイル名がコマンドラインとしてspedifiedされたときに、ためのarg、その名前がstdoutに出力されます
Peter.O

4

これは多段階なので、最適ではありませんが、ここに行きます。

1)で最も長い線の長さを見つけますfile1.txt

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

あなたの例では、最長の行は22です。

2)awkを使用してpadしfile1.txt、22行未満の各行に22文字までprintfステートメントを埋め込みます。

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

注:FSの場合、に存在しない文字列を使用しますfile1.txt

3)以前と同じようにペーストを使用します。

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

これが頻繁に行うことであれば、簡単にスクリプトに変換できます。


最も長い行を見つけるためのコードでは、が必要ですwhile IFS= read -r line。そうしないと、シェルは空白とバックスラッシュを破壊します。しかし、シェルはその仕事に最適なツールではありません。GNU coreutilsの最新バージョンにはwc -L(fredの回答を参照)、またはawk:を使用できますawk 'n<length {n=length} END {print +n}'
ジル 'SO-悪であるのをやめる'

4

グレン・ジャックマンの答えにコメントすることはできませんので、これを追加して、Peter.Oが指摘した空のセルの問題に対処します。各タブの前にヌル文字を追加すると、単一の区切りとして扱われる区切り文字の実行がなくなり、問題に対処します。(元々スペースを使用していましたが、ヌル文字を使用すると、列間の余分なスペースがなくなります。)

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

null文字がさまざまな理由で問題を引き起こす場合は、次のいずれかを試してください。

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

または

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

両方ともsedcolumnUnix / Linuxのフレーバーとバージョン、特にBSD(およびMac OS X)対GNU / Linuxで実装が異なるようです。


そのsedコマンドは何もしないようです。columnコマンドをに置き換えましたが、od -cnullバイトが表示されません。これは、centosとubuntuにあります。
グレンジャックマン

1
これはRedHat EL4で機能しました。sedとcolumnは、時間とシステムによって異なるようです。Ubuntu 14.4では\0nullin sedの使用は機能しませんでしたが、機能\x0しました。ただし、列にline too longエラーが発生しました。最も簡単なことは、スペースを使用して余分なキャラクターと一緒に暮らすことです。
テクノ

0

バハマトの答え基づいて:これはで完全に実行できawk、ファイルを一度だけ読み取り、一時ファイルを作成しません。述べられているように問題を解決するには、

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

awkこのilkの多くのスクリプトと同様に、上記は最初に読み取りfile1save配列内のすべてのデータを保存し、同時に最大行長を計算します。次にfile2 、保存された(file1)データを読み取り、現在の(file2)データと並べて印刷します。最後に、file1file2(より多くの行がある)より長い場合、最後の数行file1 (2番目の列に対応する行がない行)を印刷します。

printfフォーマットに関して:

  • "%-nns"フィールドnn文字幅で左寄せされた文字列を出力します。
  • "%-*s", nn同じことを行い*ます—は、次のパラメーターからフィールド幅を取得するように指示します。
  • for を使用して、列間に2つのスペースを取得します。明らかに調整することができます。maxlength+2nn+2

上記のスクリプトは、2つのファイルに対してのみ機能します。3つのファイルを処理するように、または4つのファイルを処理するように簡単に変更できますが、これは退屈で、演習として残されます。ただし、任意の数ファイルを処理するように変更するのは難しくないことがわかり ます。

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*

これは最初のスクリプトと非常に似ていますが、

  • それはターンmax_length配列に。
  • それはターンmax_FNR配列に。
  • これはsave2次元配列になります。
  • すべてのファイルを読み取り、すべての内容を保存します。次に、ブロックからのすべての出力をEND書き出します。

私はこの質問が古いことを知っています。私はそれにつまずいた。それpasteが最善の解決策であることに同意します。具体的には、グレンジャックマンのpaste file1 file2 | column -s $'\t' -t。しかし、awkアプローチを改善しようとするのは楽しいと思いました。
G-Manは「Reinstate Monica」と
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.