【问题标题】:Getting paste to output column-wise when input is from a pipe当输入来自管道时,按列粘贴到输出
【发布时间】:2014-08-07 07:37:28
【问题描述】:

我有一个程序在 bash 的 for 循环中处理多个数据文件。目前我需要将每个循环步骤输出到一个单独的文件,然后在最后再次处理所有这些文件以便将它们制成表格。我想避免通过文件执行此操作的步骤,并使用基本的 shell 命令来执行此操作。看起来这应该是可能的,但我已经碰壁了。以下是说明粗略过程的示例:

#create some example data
seq 1 100 > results-all
split -l 10 -d -a 1 results-all results-
#process the data
for RESULT in `seq 0 9`; do tail -n-5 results-$RESULT > results-clean-$RESULT; done; paste results-clean-{0..9}

请记住,这是一个示例,实际上我正在对这些输入文件进行更多处理,因此将该步骤移至其他地方/以某种方式移除的过于简化的解决方案在实践中不太可能起作用(这是 只是一个例子)。当我尝试更改此代码以删除通过文件的中间步骤时,我失去了粘贴以将输入格式化为列的能力。因此:

for RESULT in `seq 0 9`; do tail -n-5 results-$RESULT; done | paste - - - - - - - - - -

现在输出是按行而不是按列的。我可以转置这些数据,但似乎没有外壳工具可以做到这一点。我可以编写代码对此进行转置(或从 Stack Overflow 复制一个示例来执行此操作),但似乎应该有一种方法可以做到这一点而无需求助。

非常感谢任何帮助。

【问题讨论】:

    标签: bash shell


    【解决方案1】:

    这里的解决方案是使用读取和多个文件描述符作为输入。

    编辑:我找到了一种自动分配文件描述符的方法:在 exec 中保留它们。

    for FDNR in `seq 0 9`; do eval exec "{fd$FDNR}<results-$FDNR"; done
    while read -u $fd0 FILELINE ; do 
       echo -n $FILELINE
       for FDNR in `seq 1 9`; do 
           otherfd=fd$FDNR
           read -u ${!otherfd} FILELINE; 
           echo -n -e \\t$FILELINE
       done 
       echo
    done 
    for FDNR in `seq 0 9`; do eval exec "{fd$FDNR}<&-"; done
    

    一般的想法是您将每个输入分配给自己的文件描述符,然后从不同的文件描述符中循环读取每个文件的一行并将其输出(atm 由制表符分隔)。在你输出文件的一行之后,你做一个空的回显到下一行(在被 -n 抑制之前)。

    【讨论】:

    • 如果我要编写一些代码,只需编写矩阵转置就更容易了。看起来这应该是可以解决的而不需要使用定制脚本。
    • column 是正确的工具,但不幸的是,它是以终端为中心的观点编写的,其想法是尝试为给定终端提供最佳列输出。我只是在其中添加相关选项,并会在几分钟后在此处发布新答案...
    【解决方案2】:

    也许您可以使用paste-s 选项?

    呃……不,行不通。我们有column 实用程序:

    for RESULT in `seq 0 9`; do tail -n-5 results-$RESULT; done | column -c100
    

    但有趣的是,输出的数量或列数是由当前终端的宽度控制的! :-/

    编辑

    column -c100 为我工作以产生所需的输出,但我想这也取决于您系统上标准列表的长度...

    EDIT2

    好的,所以 OP 想要的是 column 的行为,一只猫通过它告诉它输出矩阵的高度(以行数为单位),无论终端的大小如何。我在column 中添加了-h numrows 选项,原来的column 来自BDSutils(我的Debian 上的包bsdmainutils)。

    请查找my modified version there。简单编译:

    cc -o column column.c
    

    然后运行:

    for RESULT in `seq 0 9`; do tail -n-5 results-$RESULT; done | /path/to/new/column -h5
    

    获取高度为 5 的列。此选项与 -x 标志兼容,该标志在另一个方向(即按行)填充您的矩阵。

    【讨论】:

    • paste -s results-clean-{0..9} 将做与上述类似的事情。当在 stdinput 上调用时,它只会在一行中消耗整个流。
    • 我尝试了具有各种参数的列(我认为几乎所有排列...),但无法做到这一点。也许我错过了什么?另外,对我来说,控制显示列的“-c”参数只是将它设置为一列(除非我没有设置它,在这种情况下,输出类似于你的显示宽度说)。
    • 是的,如果您不想使用中间文件,使用paste 不是正确的解决方案,因为 stdinput 只是一个很大的“列”。您必须使用 column 实用程序,请参阅我的上次编辑。
    • @Dunk 关于最后一条评论:作为参数提供给 -c 选项的整数是以字符数而不是列数表示的宽度。
    • 这似乎可行,当然它对数据的宽度非常敏感,这不是一件容易控制的事情:-/。例如,即使显示所有数据也会杀死它: for RESULT in seq 0 9;做猫结果-$RESULT;完成 |列 -c 100
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    • 2010-10-15
    • 1970-01-01
    • 2010-12-07
    相关资源
    最近更新 更多