110

2个不同长度的未排序文本文件如何并排显示(在列中)shell

给定one.txttwo.txt

$ cat one.txt
apple
pear
longer line than the last two
last line

$ cat two.txt
The quick brown fox..
foo
bar 
linux

skipped a line

展示:

apple                               The quick brown fox..
pear                                foo
longer line than the last two       bar 
last line                           linux

                                    skipped a line

paste one.txt two.txt几乎可以做到这一点,但不能很好地对齐列,因为它只是在第 1 列和第 2 列之间打印一个选项卡。我知道如何使用 emacs 和 vim 进行此操作,但希望将输出显示到 stdout 以进行管道等。

我想出的解决方案使用sdiff然后管道到 sed 以删除输出sdiff添加。

sdiff one.txt two.txt | sed -r 's/[<>|]//;s/(\t){3}//'

我可以创建一个函数并将其粘贴在我的.bashrc但肯定已经存在此命令(或者可能是更清洁的解决方案)?

4

9 回答 9

192

You can use pr to do this, using the -m flag to merge the files, one per column, and -t to omit headers, eg.

pr -m -t one.txt two.txt

outputs:

apple                               The quick brown fox..
pear                                foo
longer line than the last two       bar
last line                           linux

                                    skipped a line

See Also:

于 2012-11-12T12:44:51.757 回答
36

To expand a bit on @Hasturkun's answer: by default pr uses only 72 columns for its output, but it's relatively easy to make it use all available columns of your terminal window:

pr -w $COLUMNS -m -t one.txt two.txt

Most shell's will store (and update) your terminal's screenwidth in the $COLUMNS environment variable, so we're just passing that value on to pr to use for its output's width setting.

This also answers @Matt's question:

Is there a way for pr to auto-detect screen width?

So, no: pr itself can't detect the screenwidth, but we're helping out a bit by passing in the terminal's width via the -w option.

于 2014-05-08T13:40:53.663 回答
8

If you know the input files have no tabs, then using expand simplifies @oyss's answer:

paste one.txt two.txt | expand --tabs=50

If there could be tabs in the input files, you can always expand first:

paste <(expand one.txt) <(expand two.txt) | expand --tabs=50
于 2015-08-25T06:11:09.003 回答
6
paste one.txt two.txt | awk -F'\t' '{
    if (length($1)>max1) {max1=length($1)};
    col1[NR] = $1; col2[NR] = $2 }
    END {for (i = 1; i<=NR; i++) {printf ("%-*s     %s\n", max1, col1[i], col2[i])}
}'

Using * in a format specification allows you to supply the field length dynamically.

于 2012-11-12T10:47:54.813 回答
3

There is a sed way:

f1width=$(wc -L <one.txt)
f1blank="$(printf "%${f1width}s" "")"
paste one.txt two.txt |
    sed "
        s/^\(.*\)\t/\1$f1blank\t/;
        s/^\(.\{$f1width\}\) *\t/\1 /;
    "

Under , you could use printf -v:

f1width=$(wc -L <one.txt)
printf -v f1blank "%${f1width}s"
paste one.txt two.txt |
    sed "s/^\(.*\)\t/\1$f1blank\t/;
         s/^\(.\{$f1width\}\) *\t/\1 /;"

(Of course @Hasturkun 's solution pr is the most accurate!):

Advantage of sed over pr

You can finely choose separation width and or separators:

f1width=$(wc -L <one.txt)
(( f1width += 4 ))         # Adding 4 spaces
printf -v f1blank "%${f1width}s"
paste one.txt two.txt |
    sed "s/^\(.*\)\t/\1$f1blank\t/;
         s/^\(.\{$f1width\}\) *\t/\1 /;"

Or, for sample, to mark lines containing line:

f1width=$(wc -L <one.txt)
printf -v f1blank "%${f1width}s"
paste one.txt two.txt |
    sed "s/^\(.*\)\t/\1$f1blank\t/;
  /line/{s/^\(.\{$f1width\}\) *\t/\1 |ln| /;ba};
         s/^\(.\{$f1width\}\) *\t/\1 |  | /;:a"

will render:

apple                         |  | The quick brown fox..
pear                          |  | foo
longer line than the last two |ln| bar 
last line                     |ln| linux
                              |  | 
                              |ln| skipped a line
于 2013-01-04T14:43:11.040 回答
2

remove dynamically field length counting from Barmar's answer will make it a much shorter command....but you still need at least one script to finish the work which could not be avoided no matter what method you choose.

paste one.txt two.txt |awk -F'\t' '{printf("%-50s %s\n",$1,$2)}'
于 2012-11-12T12:17:33.753 回答
2

If you want to know the actual difference between two files side by side, use diff -y:

diff -y file1.cf file2.cf

You can also set an output width using the -W, --width=NUM option:

diff -y -W 150 file1.cf file2.cf

and to make diff's column output fit your current terminal window:

diff -y -W $COLUMNS file1.cf file2.cf
于 2015-01-12T10:12:55.153 回答
1

Find below a python based solution.

import sys

# Specify the number of spaces between the columns
S = 4

# Read the first file
l0 = open( sys.argv[1] ).read().split('\n')

# Read the second file
l1 = open( sys.argv[2] ).read().split('\n')

# Find the length of the longest line of the first file
n = len(max(l0, key=len))

# Print the lines
for i in  xrange( max( len(l0), len(l1) ) ):

    try:
        print l0[i] + ' '*( n - len(l0[i]) + S) + l1[i]
    except:
        try:
            print ' ' + ' '*( n - 1 + S) + l1[i]
        except:
            print l0[i]

Example

apple                            The quick brown fox..
pear                             foo
longer line than the last two    bar 
last line                        linux

                                 skipped a line
于 2016-01-29T15:01:00.493 回答
0
diff -y <file1> <file2>


[root /]# cat /one.txt
apple
pear
longer line than the last two
last line
[root /]# cat /two.txt
The quick brown fox..
foo
bar
linux
[root@RHEL6-64 /]# diff -y one.txt two.txt
apple                                                         | The quick brown fox..
pear                                                          | foo
longer line than the last two                                 | bar
last line                                                     | linux
于 2014-01-01T11:49:18.667 回答