如何使用500个文件csv在粘贴命令中对文件进行排序

时间:2019-01-06 12:49:12

标签: shell csv unix

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

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

当我尝试仅使用100个csv文件的脚本d时,它可以工作。但是当尝试使用500个csv文件时,出现此错误:typedef unsigned __int128 uint128; uint128 b3(uint128 n) { uint128 a=1, b=0, c=0, e=0; while (n) { if (n&1) { e = a+c+e-1; c = a+b+2*c; a = 2*a+b; } else { c = a+b+2*c; b = a+2*b; } n >>= 1; } return e; }

如何处理以上错误?

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

谢谢

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

5 个答案:

答案 0 :(得分:0)

首先制作一个无需粘贴的文件,然后使用tr将其更改为一个单行纸:

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

答案 1 :(得分:0)

您可以像这样用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对于增加列号很有用。


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

chmod +x merge

现在使用以下命令将目录更改为您的线性调频脉冲所在的位置:

cd subdirectory/where/chirps/are

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

$HOME/merge

输出将冲过屏幕。如果要将其存储在文件中,请使用:

$HOME/merge > merged.csv

答案 2 :(得分:0)

如果目标是包含 100,000 行和 500 列的文件,则应执行以下操作:

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

paste之前,可以使用附加代码将 chirps _... 输入文件排序为所需的顺序。

答案 3 :(得分:0)

错误来自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, <(paste -d, <(paste -d, <1024 files>) <1023 files>) <rest of files>以某种递归方式运行。带着一点乐趣,我想到了以下内容:

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?现在,每列代表一天。

答案 4 :(得分:0)

您可以尝试使用此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

$