【问题标题】:How to sort files in paste command with 500 files csv如何使用 500 个文件 csv 在粘贴命令中对文件进行排序
【发布时间】:2019-06-01 08:46:42
【问题描述】:

我的问题类似于 How to sort files in paste command? - 已经解决了。

我在一个文件夹中有 500 个 csv 文件(每日降雨数据),命名约定为 chirps_yyyymmdd.csv。每个文件只有 1 列(降雨量值),100,000 行,没有标题。我想按时间顺序将所有 csv 文件合并到一个 csv 中。

当我尝试这个脚本 ls -v file_*.csv | xargs paste -d, 只有 100 个 csv 文件时,它起作用了。但是当尝试使用 500 个 csv 文件时,我得到了这个错误:paste: chirps_19890911.csv: Too many open files

如何处理上述错误?

为了快速解决问题,我可以将 csv 分成两个文件夹并使用上述脚本进行处理。但是,问题是我有 100 个文件夹,每个文件夹中有 500 个 csv。

谢谢

样本数据和预期结果:https://www.dropbox.com/s/ndofxuunc1sm292/data.zip?dl=0

【问题讨论】:

  • 欢迎来到 StackOverflow!在我看来,您的问题似乎不是关于 sed、awk 或 csv,而是关于如何使用 shell。如果这不是一个真正的编程问题,那么它可能与 StackOverflow 无关。您可能需要考虑在此处关闭它并将您的问题的修订版本发布到SuperUser.comunix.stackexchange.com。还要考虑在你的 shell 中使用 for 循环。这是按顺序处理文件的规范方法。 Parsing ls usually isn't a great idea..
  • 嗨@ghoti 感谢您的回答,我已经修改了问题并删除了不必要的标签
  • 你的文件夹叫什么名字?
  • @Cyrus 文件夹名是一年,从1900到2018
  • 伟大的问题通常有一个Minimal, Complete, Verifiable Example。你能给我们展示一些示例输入和输出吗?如果paste 解决方案不起作用,其他方法可能会起作用,但很高兴知道我们正走在正确的轨道上,能够重现成功的结果。

标签: shell csv unix


【解决方案1】:

您可以像这样使用gawk...

只需一个接一个地读取所有文件并将它们保存到一个数组中。该数组由两个数字索引,第一个是当前文件中的行号 (FNR),第二个是列,每次在 BEGINFILE 块中遇到新文件时,我都会递增该列。

然后,最后,打印出整个数组:

gawk 'BEGINFILE{ ++col }                        # New file, increment column number
               { X[FNR SEP col]=$0; rows=FNR }  # Save datum into array X, indexed by current record number and col
      END      { for(r=1;r<=rows;r++){
                    comma=","
                    for(c=1;c<=col;c++){
                       if(c==col)comma=""
                       printf("%s%s",X[r SEP c],comma)
                    }
                    printf("\n")
                 }
               }' chirps*

SEP 只是一个未使用的字符,用于在索引之间进行分隔符。我使用gawk 是因为BEGINFILE 对于增加列号很有用。


将以上内容保存在您的 HOME 目录中为merge。然后启动一个终端,只需一次,使用以下命令使其可执行:

chmod +x merge

现在使用如下命令将目录更改为您的啁啾所在的位置:

cd subdirectory/where/chirps/are

现在您可以使用以下命令运行脚本:

$HOME/merge

输出将在屏幕上冲过去。如果你想在一个文件中,使用:

$HOME/merge > merged.csv

【讨论】:

  • 谢谢,但很抱歉我不熟悉上面的代码。如何使用它?将其保存为代码文件并在终端中运行?还是直接粘贴并运行?
  • 对不起,我在最后添加了如何使用它的注释。如果你被卡住了,请说。祝你好运。
  • 谢谢,我设法使用“brew install gawk”在我的 Mac 中安装了 gawk。然后按照您的指导方针,尝试执行它。已经15分钟了,还没完。我错过了什么吗?
  • 如果您没有太多内存,它不会做得很好,因为它按列将所有文件加载到内存中,然后按行打印出来,我只用您的示例数据集进行了测试它工作得很好,但我没有尝试更多。一旦它开始输出,它将几乎是即时的。
  • 嗯,好的。我会等的。
【解决方案2】:

首先制作一个不粘贴的文件,然后将该文件更改为带有tr 的oneliner:

cat */chirps_*.csv | tr "\n" "," > long.csv

【讨论】:

  • 感谢您的回答,但使用上面的脚本我得到了 1 行的结果。虽然我的每个文件的数据是 1 列和 100,000 行并且没有标题。如果我想合并 500 个文件 csv,我必须得到结果 500 列和 100,000 行
  • 在这种情况下省略trcat */chirps_*.csv &gt; long.csv 应该可以工作。您提到了paste,这就是我尝试粘贴行的原因。
  • 结果就像之前代码的转置。
【解决方案3】:

如果目标是具有 100,000 行和 500 列的文件,那么这样的东西应该可以工作:

paste -d, chirps_*.csv > chirps_500_merge.csv

附加代码可用于在pasteing 之前将chirps_... 输入文件排序为任何所需的顺序。

【讨论】:

    【解决方案4】:

    错误来自ulimit,来自man ulimit

    -n 或 --file-descriptor-count 打开文件描述符的最大数量

    在我的系统上ulimit -n 返回 1024。

    很高兴我们可以粘贴粘贴输出,所以我们可以链接它。

    find . -type f -name 'file_*.csv' | 
    sort | 
    xargs -n$(ulimit -n) sh -c '
         tmp=$(mktemp); 
         paste -d, "$@" >$tmp; 
         echo $tmp
    ' -- |
    xargs sh -c '
         paste -d, "$@"
         rm "$@"
    ' --
    
    1. Don't parse ls output
    2. 一旦我们从解析 ls 输出转移到良好查找,我们就会找到所有文件并对其进行排序。
    3. 第一个 xargs 一次取 1024 个文件,创建临时文件,将输出粘贴到临时文件中并输出临时文件文件名
    4. 第二个 xargs 对临时文件执行相同的操作,但也会删除所有临时文件
    5. 由于文件数为 100*500=500000,小于 1024*1024,因此我们只需通过一次即可。
    6. 针对生成的测试数据进行测试:

      seq 1 2000 |
      xargs -P0 -n1 -t sh -c '
          seq 1 1000 |
          sed "s/^/ $RANDOM/" \
          >"file_$(date --date="-${1}days" +%Y%m%d).csv"
      ' --
      
    7. 这个问题似乎很像foldl,一次折叠的最大块大小。基本上我们想要以递归方式运行的paste -d, &lt;(paste -d, &lt;(paste -d, &lt;1024 files&gt;) &lt;1023 files&gt;) &lt;rest of files&gt;。有趣的是,我想出了以下内容:

    func() {
            paste -d, "$@"
    }
    
    files=()
    tmpfilecreated=0
    
    # read filenames...c
    while IFS= read -r line; do
    
            files+=("$line")
    
            # if the limit of 1024 files is reached
            if ((${#files[@]} == 1024)); then
                    tmp=$(mktemp)
    
                    func "${files[@]}" >"$tmp"
    
                    # remove the last tmp file
                    if ((tmpfilecreated)); then
                            rm "${files[0]}"
                    fi
                    tmpfilecreated=1
    
                    # start with fresh files list
                    # with only the tmp file
                    files=("$tmp")
            fi
    done
    
    func "${files[@]}"
    
    # remember to clear tmp file!
    if ((tmpfilecreated)); then
            rm "${files[0]}"
    fi
    

    我猜readarray/mapfile 可能会更快,并导致代码更清晰:

    func() {
            paste -d, "$@"
    }
    
    tmp=()
    tmpfilecreated=0
    while readarray -t -n1023 files && ((${#files[@]})); do
            tmp=("$(mktemp)")
    
            func "${tmp[@]}" "${files[@]}" >"$tmp"
    
            if ((tmpfilecreated)); then
                    rm "${files[0]}"
            fi
            tmpfilecreated=1
    done
    
    func "${tmp[@]}" "${files[@]}"
    
    if ((tmpfilecreated)); then
            rm "${files[0]}"
    fi
    

    PS。 I want to merge all the csv files into a single csv in chronological order. 不就是cut 吗?现在每一列代表一天。

    【讨论】:

    • 感谢您的代码。如何使用它?将其保存为代码文件并在终端中运行?还是直接粘贴并运行?
    • 截取的第一个代码已准备好复制。其余部分 - find . -type f -name 'file_*.csv' | sort | ( .... ) - 将 ... 替换为代码 3rd 或 4rd 代码 sn-p。
    • 请原谅我的无知,我对此并不熟悉。我已经输入了所有代码。输出位置 (tmp) 在哪里?它在 $HOME 中吗?
    • 在 xargs 内的 mktemp 参数中我的测试 -p/tmp/a 有剩余,可能应该删除。
    • 它工作正常,现在仍在运行,我可以在终端中看到进度。但是如何将输出保存在文件中?假设 merge.csv 与输入数据位于同一文件夹中。
    【解决方案5】:

    你可以试试这个 Perl-one 班轮。它适用于目录下与 *.csv 匹配的任意数量的文件

    $ ls -1 *csv
    file_1.csv
    file_2.csv
    file_3.csv
    $ cat file_1.csv
    1
    2
    3
    $ cat file_2.csv
    4
    5
    6
    $ cat file_3.csv
    7
    8
    9
    
    $ perl -e  ' BEGIN { while($f=glob("*.csv")) { $i=0;open($FH,"<$f"); while(<$FH>){ chomp;@t=@{$kv{$i}}; push(@t,$_);$kv{$i++}=[@t];}} print join(",",@{$kv{$_}})."\n" for(0..$i) } '                                                                              <
    1,4,7
    2,5,8
    3,6,9
    
    $
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-14
      • 1970-01-01
      • 1970-01-01
      • 2019-04-06
      • 1970-01-01
      • 1970-01-01
      • 2017-06-05
      相关资源
      最近更新 更多