更好的粘贴命令


11

我有以下两个文件(我用点填充了行,因此文件中的每一行都具有相同的宽度,并将file1设置为大写,以使其更加清晰)。

contents of file1:

ETIAM......
SED........
MAECENAS...
DONEC......
SUSPENDISSE

contents of file2

Lorem....
Proin....
Nunc.....
Quisque..
Aenean...
Nam......
Vivamus..
Curabitur
Nullam...

请注意,file2比file1长。

当我运行此命令时:

paste file1 file2

我得到这个输出

ETIAM...... Lorem....
SED........ Proin....
MAECENAS... Nunc.....
DONEC...... Quisque..
SUSPENDISSE Aenean...
    Nam......
    Vivamus..
    Curabitur
    Nullam...

我要怎么做才能使输出如下?

ETIAM...... Lorem....
SED........ Proin....
MAECENAS... Nunc.....
DONEC...... Quisque..
SUSPENDISSE Aenean...
            Nam......
            Vivamus..
            Curabitur
            Nullam...

我试过了

paste file1 file2 | column -t

但是这样做:

ETIAM......  Lorem....
SED........  Proin....
MAECENAS...  Nunc.....
DONEC......  Quisque..
SUSPENDISSE  Aenean...
Nam......
Vivamus..
Curabitur
Nullam...

不如原始输出丑陋,但无论如何列错误。


2
paste正在使用第二个文件中各行前面的选项卡。您可能必须使用后处理器来适当地对齐列。
unxnut

3
paste file1 file2 | column -tn
ninjalj 2013年

file1总是具有固定大小的列吗?
RSFalcon7 2013年

@ RSFalcon7是的,确实如此。
TulainsCórdova13年

Answers:


17

假设您的文件中没有任何制表符,

paste file1 file2 | expand -t 13

arg可以-t适当选择以覆盖file1中所需的最大行宽。

OP添加了更灵活的解决方案:

我这样做是为了在没有魔术数字13的情况下可以正常工作:

paste file1 file2 | expand -t $(( $(wc -L <file1) + 2 ))

键入不容易,但可以在脚本中使用。


真好!在阅读您的答案之前,我不了解扩展:)
TabeaKischka

4

我以为awk可能做得很好,所以我用Google搜索“从两个文件中读取输入”,并找到了一篇关于stackoverflow的文章作为起点。

首先是精简版,然后在下面进行完全注释。这花费了超过几分钟的时间来解决。我为更聪明的人所做的一些改进而感到高兴。

awk '{if(length($0)>max)max=length($0)}
FNR==NR{s1[FNR]=$0;next}{s2[FNR]=$0}
END { format = "%-" max "s\t%-" max "s\n";
  numlines=(NR-FNR)>FNR?NR-FNR:FNR;
  for (i=1; i<=numlines; i++) { printf format, s1[i]?s1[i]:"", s2[i]?s2[i]:"" }
}' file1 file2

这是上面的完整文档版本。

# 2013-11-05 mike@diehn.net
# Invoke thus:
#   awk -f this_file file1 file2
# The result is what you asked for and the columns will be
# determined by input file order.
#----------------------------------------------------------
# No matter which file we're reading,
# keep track of max line length for use
# in the printf format.
#
{ if ( length($0) > max ) max=length($0) }

# FNR is record number in current file
# NR is record number over all
# while they are equal, we're reading the first file
#   and we load the strings into array "s1"
#   and then go to the "next" line in the file we're reading.
FNR==NR { s1[FNR]=$0; next }

# and when they aren't, we're reading the
#   second file and we put the strings into
#   array s2
{s2[FNR]=$0}

# At the end, after all lines from both files have
# been read,
END {
  # use the max line length to create a printf format
  # the right widths
  format = "%-" max "s\t%-" max "s\n"
  # and figure the number of array elements we need
  # to cycle through in a for loop.
  numlines=(NR-FNR)>FNR?NR-FNR:FNR;
  for (i=1; i<=numlines; i++) {
     printf format, s1[i]?s1[i]:"", s2[i]?s2[i]:""
  }
}

1
+1是唯一适用于任意输入(即可能包含制表符的行)的答案。我认为这不会得到明显改善。
don_crissti

2

这不是一个很好的解决方案,但是我能够使用

paste file1 file2 | sed 's/^TAB/&&/'

TAB替换为制表符。


&&sed命令中的作用是什么?
coffeMug ​​2013年

1
单个&放置要搜索的内容(在这种情况下为选项卡)。此命令仅将开头的选项卡替换为两个选项卡。
unxnut

我必须进行更改TAB\t以使其在Ubuntu debian上的zsh中工作。而且仅在file1的字符数少于15个时才起作用
rubo77

2

在Debian和衍生产品上,column有一个-n nomerge选项,该选项允许列对空字段执行正确的操作。在内部,column使用wcstok(wcs, delim, ptr)函数,该函数将宽字符串分成由delim参数中的宽字符分隔的标记。

wcstok首先delim在识别令牌之前跳过中的宽字符。该-n选项使用的算法不会跳过中的初始宽字符delim

不幸的是,这不是很可移植:-n是Debian特定的,并且column不在POSIX中,这显然是BSD的东西。


2

取出用于填充的点:

文件1:

ETIAM
SED
MAECENAS
DONEC
SUSPENDISSE

文件2:

Lorem
Proin
Nunc
Quisque
Aenean
Nam
Vivamus
Curabitur
Nullam

尝试这个:

$ ( echo ".TS"; echo "l l."; paste file1 file2; echo ".TE" ) | tbl | nroff | more

您将获得:

ETIAM         Lorem
SED           Proin
MAECENAS      Nunc
DONEC         Quisque
SUSPENDISSE   Aenean
              Nam
              Vivamus
              Curabitur
              Nullam

就像其他使用解决方案的方法一样,paste如果有任何行包含制表符,则将无法打印正确的输出。+1是与众不同的
don_crissti

+1。您能否解释一下该解决方案的工作原理?
图兰斯·科尔多瓦

1

一种awk解决方案,应具有相当的可移植性,并且适用于任意数量的输入文件:

# Invoke thus:
#   awk -F\\t -f this_file file1 file2

# every time we read a new file, FNR goes to 1

FNR==1 {
    curfile++                       # current file
}

# read all files and save all the info we'll need
{
    column[curfile,FNR]=$0          # save current line
    nlines[curfile]++               # number of lines in current file
    if (length > len[curfile])
            len[curfile] = length   # max line length in current file
}

# finally, show the lines from all files side by side, as a table
END {
    # iterate through lines until there are no more lines in any file
    for (line = 1; !end; line++) {
            $0 = _
            end = 1

            # iterate through all files, we cannot use
            #   for (file in nlines) because arrays are unordered
            for (file=1; file <= curfile; file++) {
                    # columnate corresponding line from each file
                    $0 = $0 sprintf("%*s" FS, len[file], column[file,line])
                    # at least some file had a corresponding line
                    if (nlines[file] >= line)
                            end = 0
            }

            # don't print a trailing empty line
            if (!end)
                    print
    }
}

如何在file1和file2上使用它?我调用了脚本paste-awk并尝试了paste file1 file2|paste-awkawk paste-awk file1 file2但没有尝试。
rubo77

我得到了awk: Line:1: (FILENAME=file1 FNR=1) Fatal: Division by zero
rubo77

@ rubo77:awk -f paste-awk file1 file2应该至少对GNU awk和mawk有效。
ninjalj 2013年

尽管这paste与两行之间的空间稍有不同,但是可以起作用。而且,如果输入文件的所有行的长度都不相同,则将导致右对齐行
rubo77

@ rubo77:字段分隔符可以设置为-F\\t
ninjalj 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.