如何使用awk分割文件并将每个文件名存储在Bash数组中

如何使用awk分割文件并将每个文件名存储在Bash数组中,bash,csv,awk,Bash,Csv,Awk,输入 一个名为input\u file.csv的文件,它有7列和n行 标题和行示例: Date Location Team1 Team2 Time Prize_$ Sport 2016 NY Raptors Gators 12pm $500 Soccer 输出 n个文件,其中每个新文件中的行根据原始文件第7列中的值进行分组。每个文件都以第7列中的共享值命名。注意:每个文件都有相同的头。(脚本当前执行此操作。) 示例:如果原始文件中的两行将golf作为第7列的值,则它们将被分组到一个名为golf

输入

一个名为
input\u file.csv
的文件,它有7列和n行

标题和行示例:

Date Location Team1 Team2 Time Prize_$ Sport
2016 NY Raptors Gators 12pm $500 Soccer
输出

  • n个文件,其中每个新文件中的行根据原始文件第7列中的值进行分组。每个文件都以第7列中的共享值命名。注意:每个文件都有相同的头。(脚本当前执行此操作。)

    示例:如果原始文件中的两行将
    golf
    作为第7列的值,则它们将被分组到一个名为
    golf.csv
    的文件中。如果其他3行共享了
    soccer
    作为第7列的值,那么它们将在
    soccer.csv
    中找到

  • 一个数组,其中包含每个生成文件的名称。此阵列不在awk范围内。(这就是我需要帮助的地方。)

    示例:Array=[golf.csv,soccer.csv]

  • 形势

    以下脚本生成所需的输出。但是,我想在每个新生成的文件上运行另一个脚本,我不知道如何运行

    问题:

    我的想法是将每个新文件的名称存储在一个数组中。这样,我就可以在数组中循环,并对每个文件执行我想要的操作。下面的代码将名为
    array
    的变量传递到awk中,但我不知道如何将每个文件的名称添加到数组中

    #!/bin/bash
    
    ARRAY=()
    
    awk -v myarray="$ARRAY" -F"\",\"" 'NR==1 {header=$0}; NF>1 && NR>1 {if(! files[$7]) {print header >> ("" $7 ".csv"); files[$7]=1}; print $0 >> ("" $7 ".csv"); close("" $7 ".csv");}' input_file.csv
    
    for i in "${ARRAY[@]}"
        do
        :
        echo $i
    done
    

    我只是一时兴起,没有经过测试,因为你没有提供太多的样本数据,这个怎么样

    #!/usr/bin/awk -f
    
    FNR==1 {
      header=$0
      next
    }
    
    ! $7 in files {
      files[$7]=sprintf("sport-%s.csv", $7)
      print header > file
    }
    
    {
      files[$7]=sprintf("sport-%s.csv", $7)
    }
    
    {
      print > files[$7]
    }
    
    END {
      printf("declare -a sportlist=( ")
      for (sport in files) {
        printf("\"%s\"", sport)
      }
      printf(" )\n");
    }
    
    这里的想法是,我们将运动名称存储在数组
    files[]
    中,并从该数组中构建文件名。(您可以在
    sprintf()
    中设置文件名的格式,如您认为合适的话。)我们一步一步地浏览该文件,每当我们得到一个没有记录文件名的新运动时,就添加一个标题行。然后,对于非标题,根据运动名称打印到文件中

    对于第二个问题,将数组导出回awk之外的内容,这里的
    END
    块将输出一个
    declare
    行,该行可以由bash解释。如果幸运的话,您可以在命令扩展中
    eval
    这个awk脚本,并且
    declare
    命令将由shell有效地解释:

    eval $(/path/to/awkscript inputfile.csv)
    
    或者,如果您订阅的思想流派认为
    eval
    是邪恶的,您可以将awk脚本的标准输出重定向到一个临时文件,您可以从中获取:

    /path/to/awkscript inputfile.csv > /tmp/yadda.$$
    . /tmp/yadda.$$
    

    (不要使用此临时文件,使用mktemp或类似工具制作一个真实的文件。)

    与其努力让
    awk
    填充shell数组变量,不如:

    • 确保在干净的目录中创建
      *.csv
      文件
    • 使用globbing循环该目录中的所有
      *.csv
      文件

    任何程序都无法修改父shell的环境。只需让awk脚本将文件名作为标准输出输出,并使用命令替换将它们放入数组中即可

    filesArray=($(awk ... ))
    
    如果文件中可能有空格,则需要不同的解决方案;假设您使用的是bash 4,您只需确保在单独的一行中打印每个文件,并使用
    readarray

    readarray filesArray < <( awk ... )
    

    readarray filesArray<例如,如果文件不大,可以运行另一个脚本来获取唯一的$7元素

    $ awk 'NR>1&&!a[$7]++{print $7}' sports
    
    将打印值,您也可以将其更改为您的文件名格式,例如

    $ awk 'NR>1&&!a[$7]++{print tolower($7)".csv"}' sports
    
    然后可以通过管道将其传送到其他进程,例如,传送到
    wc

    $ awk ... sports | xargs wc
    

    这将实现我认为您想要的:

    oIFS="$IFS"; IFS=$'\n'
    array=( $(awk '{out=$7".csv"; print > out} !seen[out]++{print out}' input_file.csv) )
    IFS="$oIFS"
    
    如果您的输入文件确实是逗号分隔的,而不是问题中示例输入中显示的空格分隔的,则调整awk脚本以适应(您可能希望查看GNU awk和FPAT)

    如果您没有GNUawk,那么您需要添加更多的代码来关闭打开的输出文件


    如果文件名中包含换行符,则上述操作将失败,但对于空白字符或其他空格则可以。

    链接的答案没有解释如何将每个文件名添加到数组中。我尝试导出到一个文件,但没有一个文件名存储在任何地方。如果我知道如何将每个文件名添加到一个数组中,我想我可以知道如何在awk之外访问该数组。
    如何存储每个文件的名称
    -什么文件?如果你能提供更好的解释和简洁,可测试的样本输入和预期的输出I将考虑投票重新打开,但在它看来,你的问题是关闭的,因为DUP的DUP包含了你的问题的答案。@ EdMorton是这个编辑更清晰吗?是的,但是我不明白为什么你会张贴一个空间分隔的输入文件,当你说你的真正的一个是逗号分隔的。为什么你没有创建一个输入文件,比如说多出几行,然后输出你想要的文件;我想从那个输入文件中生成,使它100%清晰。哦,我想我现在知道你想要什么了。如果你还想这样做,你甚至可以在以后将文件放入数组:
    filesArray=(*.csv)
    Ya,这很有意义。但这并没有解决OP在问题中所描述的挑战,是吗?@ghoti:这个答案提出了解决问题的另一种方法(希望在这方面是明确的),因为我怀疑这个问题是一个例子。我同意这是一个XY问题——但是,我们在这里回答的80%的问题可能是,如果你看得够深。为什么OP需要按
    $7
    分开的文件?阵列的用途是什么?我相信我们可以找到更好的方法来实现他的目标。但我在他的问题中没有看到这些目标。
    oIFS="$IFS"; IFS=$'\n'
    array=( $(awk '{out=$7".csv"; print > out} !seen[out]++{print out}' input_file.csv) )
    IFS="$oIFS"