web-dev-qa-db-ja.com

列ごとにテキストファイルを組み合わせる

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)
54
Tim

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

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

「空のセル」の論争に対処するには、column-nオプションが必要です。

$ 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

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

71
glenn jackman

便利なdandy prコマンドを探しています。

paste file1 file2 | pr -t -e24

「-e24」は「タブ位置を24スペースに拡張する」です。さいわい、pasteは列の間にタブ文字を挿入するため、prはタブ文字を展開できます。 「再帰的に列挙可能」の文字を数え、2を追加することにより、24を選択しました。

12
Bruce Ediger

pdate:ここでは、表形式の出力用の非常に単純なスクリプト(質問の最後にあるスクリプト)を示します。 pasteと同じようにファイル名を渡すだけです。フレームを作成するためにhtmlを使用するので、調整可能です。複数のスペースが保持され、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アートの表形式のプレゼンテーションを作成するスクリプトです(Unicodeは期待される幅にレンダリングできない場合があることに注意してください。たとえば、௵は単一の文字です。これは列とはかなり異なります上記のいくつかのユーティリティの場合と同様に、数値が間違っています。)...以下に示すスクリプトの出力は、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を使用してvisible文字.(この例の場合のみ)... pasteTab char ...で2つの列を結合するには...

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...........
9
Peter.O

あと少しです。 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
_

これはマルチステップなので、最適ではありませんが、ここにいきます。

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

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

あなたの例では、最長のラインは22です。

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

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

これがよく行うことであれば、簡単にスクリプトに変えることができます。

4
bahamat

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

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

sedcolumnは、Unix/Linuxのフレーバーとバージョン、特にBSD(およびMac OS X)とGNU/Linuxのバージョン間で実装が異なるようです。

4
techno

バハマトの答え に基づいて構築:これは完全に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

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

printf形式について:

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

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

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を配列に変換します。
  • saveを2次元配列に変換します。
  • allファイルを読み取り、all内容を保存します。次にallENDブロックからの出力を書き出します。