【问题标题】:separate columns of a text file文本文件的单独列
【发布时间】:2023-03-04 19:51:01
【问题描述】:

各位专家,我有一个包含许多列的大文本文件。现在我想在单独的文本文件中连续提取每一列,并在顶部添加两个字符串。

假设我有一个像这样的输入文件

2 3 4 5 6 
3 4 5 6 7
2 3 4 5 6
1 2 2 2 2

然后我需要在单独的文本文件中提取每一列,顶部有两个字符串

file1.txt      file2.txt     .... filen.txt

s=5            s=5
r=9            r=9
2              3
3              4
2              3
1              2

我尝试了以下脚本:但它不能正常工作。需要专家的帮助。提前致谢。

#!/bin/sh
for i in $(seq 1 1 5)

do
echo $i
awk '{print $i}' inp_file  > file_$i
done

【问题讨论】:

    标签: linux bash for-loop awk


    【解决方案1】:

    您能否尝试在 GNU awk 中使用所示示例进行跟踪、编写和测试。以下没有使用 close 文件函数,因为您的示例显示您在 Input_file 中只有 5 列。还创建了 2 个awk 变量,这些变量将在实际列值打印到输出文件之前打印(命名为var1var2)。

    awk -v var1="s=5" -v var2="r=9" '
    {
      count++
      for(i=1;i<=NF;i++){
        outputFile="file"i".txt"
        if(count==1){
          print (var1 ORS var2) > (outputFile)
        }
        print $i > (outputFile)
      }
    }
    ' Input_file
    

    如果您可以拥有超过 5 列或更多列,则最好使用 close 选项关闭输出文件 kin 后端,然后使用此选项(以避免错误 too many files opened)。

    awk -v var1="s=5" -v var2="r=9" '
    {
      count++
      for(i=1;i<=NF;i++){
        outputFile="file"i".txt"
        if(count==1){
          print (var1 ORS var2) > (outputFile)
        }
        print $i >> (outputFile)
      }
      close(outputFile)
    }
    '  Input_file
    

    【讨论】:

      【解决方案2】:

      使用 GNU awk 在内部处理十几个左右同时打开的文件:

      NR == 1 {
          for (i=1; i<=NF; i++) {
              out[i] = "file" i ".txt"
              print "s=5" ORS "r=9" > out[i]
          }
      }
      {
          for (i=1; i<=NF; i++) {
              print $i > out[i]
          }
      }
      

      或使用任何 awk 随时关闭它们:

      NR == 1 {
          for (i=1; i<=NF; i++) {
              out[i] = "file" i ".txt"
              print "s=5" ORS "r=9" > out[i]
              close(out[i])
          }
      }
      {
          for (i=1; i<=NF; i++) {
              print $i >> out[i]
              close(out[i])
          }
      }
      

      【讨论】:

        【解决方案3】:

        使用awk 使用其输出重定向一次性通过文件非常简单:

        awk 'NR==1 { for (n = 1; n <= NF; n++) print "s=5\nr=9" > ("file_" n) }
             { for (n = 1; n <= NF; n++) print $n > ("file_" n) }' inp_file
        

        【讨论】:

        • 注意在某些awk中这是一个语法错误,需要写print $n &gt; ("file_" n)
        • 我刚刚在 MacOS 上使用 BSD awk 进行了测试,在创建第 17 个输出文件后它在那里失败了。也许您正在考虑可能的最大参数而不是最大打开文件数?
        • 如果您有一个特定的命令希望我运行,请随时分享该命令是什么,我会发布结果。这不仅仅是我的问题,这是一个常见问题。
        • ulimit -aulimit -n 在同一个框中都输出 256。您希望我运行哪个命令来告诉我们 awk 命令可以打开多少个文件?抱歉不知道。
        • “提高极限”到底是怎么回事?这是一个修辞问题,因为期望每个人都做任何事情是不合理的。您似乎认为这只是我的盒子上的一个问题 - 它不是,这是非 gawk awks 的常见问题(请参阅google.com/search?q=awk+too+many+open+files 的结果)并且它具有众所周知的解决方案,即在您关闭文件时去吧。
        【解决方案4】:

        split -nr/$(wc -w &lt;(head -1 input) | cut -d' ' -f1) -t' ' --additional-suffix=".txt" -a4 --numeric-suffix=1 --filter "cat &lt;(echo -e 's=5 r=9') - | tr ' ' '\n' &gt;\$FILE" &lt;(tr -s '\n' ' ' &lt;input) file

        这以独特的方式使用漂亮的split 命令重新排列列。希望它比awk 更快,尽管在花费大量时间对其进行编码、测试和编写之后,我发现它可能对您来说不够可扩展,因为它需要每列一个进程,而且许多系统都是受限于用户进程(检查ulimit -u)。我提交它是因为它可能对您或未来的读者有一些有限的学习用处。

        解码:

        split -- 将文件分成子文件。通常这是按行或按大小,但我们正在调整它以使用列。

        -nr/$(...) -- 使用循环输出:以循环方式将记录(在我们的例子中,矩阵单元)排序到适当数量的箱中。这是完成这项工作的关键。 parens 中的部分表示,计算(wc)输入的第一行(&lt;(head -1 input))中的字数(-w)并丢弃文件名(cut -d' ' -f1),并将输出插入到命令中行。

        -t' ' -- 使用单个空格作为记录分隔符。这会将矩阵单元格拆分为 split 的记录以进行拆分。

        --additional-suffix=".txt" -- 将.txt 附加到输出文件。

        -a4 -- 使用四位数字;您可能不会从中获得 1,000 个文件,但以防万一……

        --numeric-suffix=1 -- 添加数字后缀(通常是字母组合)并从 1 开始。这很迂腐,但与示例匹配。如果您有超过 100 列,则需要添加 -a4 选项或您需要的任何长度。

        --filter ... -- 通过 shell 命令对每个文件进行管道传输。

        外壳命令:

        cat -- 连接接下来的两个参数。

        &lt;(echo -e 's=5 r=9') -- 这意味着执行 echo 命令并将其输出用作cat 的输入。我们使用空格而不是换行符来分隔,因为我们最终会将空格转换为换行符,而且它更短且更易于阅读。

        - -- 读取标准输入作为cat 的参数 -- 这是分箱数据。

        | tr ' ' '\n' -- 根据所需的输出示例,将记录之间的空格转换为换行符。

        &gt;\$FILE -- 写入输出文件,该文件存储在$FILE 中(但我们必须引用它,以便 shell 不会在初始命令中解释它)。

        Shell 命令结束 -- split 参数的其余部分:

        &lt;(tr -s '\n' ' ' &lt; input) -- 使用示例输入文件作为split 的输入,但将换行符转换为空格,因为我们不需要它们并且我们需要一致的记录分隔符。 -s 表示每条记录之间只输出一个空格(以防我们在输入时有多个空格)。

        file -- 这是输出文件名的前缀。我的示例中的输出将是file0001.txtfile0002.txt、...、file0005.txt

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-02-09
          • 1970-01-01
          • 2023-04-05
          • 1970-01-01
          • 2021-08-19
          相关资源
          最近更新 更多