如何在Bash中依次打印列?

3

有没有更好的方法将两列或更多列打印成一列,例如

input.file

AAA    111
BBB    222
CCC    333

输出:

AAA
BBB
CCC
111
222
333

我只能想到:

cut -f1 input.file >output.file;cut -f2 input.file >>output.file

但是如果有很多列,或者当我想将输出导入到其他命令(如sort)时,这并不好。

还有其他建议吗?非常感谢!


1
所有行中的列数始终相同吗? - fedorqui
@fedorqui:是的,我也一样。 - once
3
如果你在提问时说得更清楚一些,比如这样:我不关心顺序,我只是想在排序和去重之后进行操作,那么你可能会得到一个更简单的解决方案。 - Sundeep
2
例如:sed 's/\s\+/\n/g' input.file - Sundeep
1
@sp asic:你说得对,我没有考虑顺序,我认为应该有一种非常简单的方法将整个第二列打印出来,所以我这样问问题。然而,我认为我应该保持这个问题的方式,因为回答更困难的问题更有价值。 - once
显示剩余6条评论
7个回答

6
使用 awk
awk '{if(maxc<NF)maxc=NF;
      for(i=1;i<=NF;i++){(a[i]!=""?a[i]=a[i]RS$i:a[i]=$i)}
      }
     END{
      for(i=1;i<=maxc;i++)print a[i]
     }' input.file

它能工作!谢谢!(a!=""?a=a"\n"$2:a=$2) 的意思是什么? - once
将换行符 \n 转换为 ( \t 或 \n)? - once

5
你可以使用GNU awk的数组嵌套数组来存储所有数据并在以后打印出来。
如果列数是恒定的,那么这适用于任何数量的列:
gawk '{for (i=1; i<=NF; i++)            # loop over columns
           data[i][NR]=$i               # store in data[column][line]
      }
      END {for (i=1;i<=NR;i++)          # loop over lines
                for (j=1;j<=NF;j++)     # loop over columns
                     print data[i][j]   # print the given field
      }' file

注意:NR代表记录数(即此处的行数),NF代表字段数(即给定行中的字段数)。

如果列数在行之间发生变化,那么我们应该使用另一个数组,用于存储每行的列数。但是在问题中我没有看到对此的要求,所以现在我暂时不进行处理。

以下是具有三列的示例:

$ cat a
AAA    111  123
BBB    222  234
CCC    333  345
$ gawk '{for (i=1; i<=NF; i++) data[i][NR]=$i} END {for (i=1;i<=NR;i++) for (j=1;j<=NF;j++) print data[i][j]}' a
AAA
BBB
CCC
111
222
333
123
234
345

如果列数不是固定的,使用一个数组来存储每行的列数有助于跟踪它:
$ cat sc.wk 
{for (i=1; i<=NF; i++)
       data[i][NR]=$i
 columns[NR]=NF
}
END {for (i=1;i<=NR;i++)
            for (j=1;j<=NF;j++)
                 print (i<=columns[j] ? data[i][j] : "-")
}
$ cat a
AAA    111  123
BBB    222
CCC    333  345
$ awk -f sc.wk a
AAA
BBB
CCC
111
222
333
123
-
345

2
嘿,为什么要给我点踩?是有什么我漏掉的吗? - fedorqui
唯一想到的是,由于所有索引都是数字,可移植的 data[i,j]data[i][j] 一样容易使用。 - kdhp
@kdhp 很好的观点。是的,这是GNU awk特定的,因为我正在使用数组的数组。 - fedorqui

3
awk '{print $1;list[i++]=$2}END{for(j=0;j<i;j++){print list[j];}}' input.file

输出

AAA
BBB
CCC
111
222
333

更简单的解决方案是:
 awk -v RS="[[:blank:]\t\n]+" '1' input.file

1
期望制表符作为分隔符:
$ cat <(cut -f 1 asd) <(cut -f 2 asd)
AAA
BBB
CCC
111
222
333

1
嗯,当你给一个负评时,习惯上要加上评论。 - James Brown
James Brown 提出的第二个解决方案更好,不会在输入中混合列计数时中断。 - louigi600
确实如此。这里只是为了展示一种更好的构建原始想法的方式。 - James Brown
我同样点赞了另一个答案,所以净总分是中性的……只是希望另一个解决方案能得到更好的认可。 - louigi600

1

由于顺序无关紧要:

$ awk 'BEGIN {RS="[ \t\n]+"} 1' file
AAA
111
BBB
222
CCC
333

哦,看一下问题。它在哪里提到顺序不重要? - Мона_Сах
@mona_sax:在第5条评论中,对于我的情况,我不关心顺序。 - James Brown
1
嗯,当你给一个负评时,习惯上要加上评论。 - James Brown
抱歉,我没有看到那个评论。 - Мона_Сах

0

这将适用于任意数量的以空格分隔的列

awk  '{for (A=1;A<=NF;A++) printf("%s\n",$A);}' input.file | sort -u > output.file

如果空格不是分隔符...让我们假设":"是分隔符
awk -F: '{for (A=1;A<=NF;A++) printf("%s\n",$A);}' input.file | sort -u > output.file

0

丑陋,但它能用-

for i in {1..2} ; do awk -v p="$i" '{print $p}' input.file ; done

{1..2}更改为{1..n},其中'n'是输入文件中列数的数量

解释-

我们定义一个变量p,它本身就是变量i。 i的值从1到n变化,在每个步骤中,我们打印文件的第'i'列。


根据您想要迭代的列数,需要修复的代码,请查看我的解决方案。 - louigi600
为什么我被踩了?到目前为止,我的解决方案是唯一一个给出正确输出的。 - Chem-man17
我没有点踩!但我猜人们正在寻找更高效的方法,只使用awk本身,就像两个正分数答案! - Inian
1
@louigi600,我在我的回答中清楚地提到,要想有多列,你只需要将2改为“n”。你在给我负评之前没有读过我的回答吗? - Chem-man17
我愿意承认你不喜欢每次更改“n”的值。但恐怕你错了,这段代码不会因为“列数不同”而失败。但没关系,就让它过去吧。 - Chem-man17
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接