【问题标题】:how to set field name as file name bash/awk如何将字段名设置为文件名bash/awk
【发布时间】:2016-06-06 16:41:50
【问题描述】:

我有一个包含 500 列的文件,我需要将每一列拆分为一个新文件,同时在所有文件中打印 $1。下面是一个示例文件,我设法使用下面的 bash/awk 解决方案来做到这一点:

ID    F1    F2    F4    F4
aa    1    2    3    4 
bb    1    2    3    4
cc    1    2    3    4
dd    1    2    3    4

num=('1' '2' '3' '4')
for i in ${num[@]}; do awk -F "\t" -v col="$i" '{print $1,$col}' OFS="\t"        
Input.txt > ${i}.txt; done

它给出了所需的输出:

1.txt
ID    ID
aa    aa
bb    bb
cc    cc
dd    dd

2.txt
ID    F1
aa    1
bb    1
cc    1
dd    1

....

但是,我无法跟踪哪个文件对应于哪个列,因为输出文件名是字段编号而不是字段名称。是否可以将字段的标题作为输出文件名的前缀写入?

ID.txt
ID    ID
aa    aa
bb    bb
cc    cc
dd    dd

F1.txt
ID    F1
aa    1
bb    1
cc    1
dd    1

【问题讨论】:

  • 原则上,它可以在awk 中一次完成,但是 500 列可能是个问题——您需要至少能够打开 500 个文件才能一次完成(我的ulimit -n 默认是256)。您可能需要多次通过(例如第 2-99、100-199、200-299、300-399、400-499 列)或其他任何内容。
  • 读取第一行并用作for循环的索引,用户自己的计数器来计数列

标签: bash awk


【解决方案1】:

您可以在一个 awk 脚本中完成所有操作。处理第一行时,将所有列标题放入一个数组中。然后,当您处理行时,您会循环写入该数组中的文件名。

awk -F'\t' 'NR == 1 { split($0, filenames) }
     {for (col = 1; col <= NF; col++) { 
        file= filenames[col] ".txt"; 
        print $1, $col >> file; 
        close(file) } }' Input.txt

【讨论】:

  • 不要错过每一行之后的close($filenames[col])。否则你可能会用完文件描述符。
  • 现在大多数系统都有数千个描述符,所以这不太可能成为问题,但我添加了它。我也改成&gt;&gt;了,因为我要关闭文件,有必要吗?
  • 现在大多数文件都有数百万行;)
  • awk -F'\t' 'NR == 1 { split($0, filenames) }{for (col = 1; col &lt;= NF; col++) print $1, $col &gt;&gt; $filenames[col]".txt"; close($filenames[col]".txt")}' Genes.txt awk: syntax error at source line 1 context is NR == 1 { split($0, filenames) }{for (col = 1; col &lt;= NF; col++) print $1, $col &gt;&gt; &gt;&gt;&gt; $filenames[col]".txt" &lt;&lt;&lt; awk: illegal statement at source line 1
  • @hek2mgl 行数不是问题,列数才是问题。他说大约是 500。
【解决方案2】:

如果我正确理解您的要求,您似乎已经很接近了。试试

num=('1' '2' '3' '4')
for i in ${num[@]}; do
  echo "i=$i"
  awk -F "\t" -v col="$i" -v OFS="\t" '
    NR==1{fName=$(col+1)".out";next}
    {print $1,$(col+1) > fName}' data.txt
done   

1>cat F1.out
aa      1
bb      1
cc      1
dd      1

. . . .

1>cat F4.out
aa      4
bb      4
cc      4
dd      4

编辑

如果您需要保留示例输出中显示的标题,只需删除 ;next


编辑 2

如果您有多个同名的列,您可以改用&gt;&gt; fName 将数据附加到同一个文件中。用这种技术警告一句话。当您使用&gt; fName 时,这会在您每次重新运行脚本时“重新启动”文件。但是当使用&gt;&gt; 时,您将在每次运行脚本时附加到每个文件。这可能会导致下游进程出现问题 ;-) ...因此您需要添加代码来清理您之前运行的脚本。


在这里,我们依赖于 awk 也可以使用 &gt; fName 将输出写入文件的事实(其中 fName 已定义为 col(Num)+1 的值(跳过第一列值)。

而且,如果您打算每天执行数千次,则值得进一步优化上述每个 cmets,让 awk 读取一次文件并从内部循环创建所有输出。但是,如果您只需要这样做几次,那么您的“使用 unix/linux 的工具将任务分解为可管理的部分”是非常合适的。

IHTH

【讨论】:

  • 不要错过每一行之后的close(fName)。否则你可能会用完文件描述符。
  • @hek2mgl :嗯..我的经验是 1 &gt; fName 仅在 awk 调用的生命周期内保持活动状态。由于我们一次只处理一列,这不工作吗?谢谢,祝大家好运。
  • 如果列名称不同,则有效。我一列可以出现多次,awk 将保持该文件打开。如果您有 n 个不同的列 > 文件描述符,它将失败。
  • col+1?看起来他想打印输入整数的列。
  • 它需要将每一列打印到一个文件中,每个文件的标准第一列是 $1。即 file1 应该有 $1,$1,file2 应该有 $1,$2,file3 应该有 $1,$3 .....,每列的标题作为文件名。即使用 $1 标头作为 file1 的文件名,使用 $2 标头作为 file2 的文件名 ....
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-23
  • 1970-01-01
  • 2017-10-29
  • 2017-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多