我的问题类似于 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
答案 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 "$@"
' --
针对使用以下命令生成的测试数据进行了测试:
seq 1 2000 |
xargs -P0 -n1 -t sh -c '
seq 1 1000 |
sed "s/^/ $RANDOM/" \
>"file_$(date --date="-${1}days" +%Y%m%d).csv"
' --
问题似乎很像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
$