我正在尝试使用模拟数据生成 csv
for i in {1..1000000..1}
do
echo "$i,$(date -d "2017-08-01 + $(shuf -i 1-31 -n 1) days" +'%Y-%m-%d')" >> $F
done;
从 1 到百万循环并生成唯一的 id &随机的日期
但它运行速度非常慢,有什么单线可以使其平行吗?
答案1
最终结果请参见最后。
for i in {1..1000000..1}
do
echo "$i,$(date -d "2017-08-01 + $(shuf -i 1-31 -n 1) days" +'%Y-%m-%d')" >> $F
done;
Shell 循环很慢,有两个主要因素导致这个特定的循环特别慢:
- 在每次迭代中打开并附加到文件。
- 每次迭代中两次执行外部实用程序(
shuf
和)。date
它echo
可能内置于 shell 中,因此会产生更少的开销。
输出重定向最容易修复:
for i in {1..1000000..1}
do
echo "$i,$(date -d "2017-08-01 + $(shuf -i 1-31 -n 1) days" +'%Y-%m-%d')"
done >"$F"
这只会打开输出文件一次,并在循环期间保持打开状态。
其余的代码可以使用awk
GNU更有效地完成date
(因为您正在使用shuf
我假设您使用的是 Linux 系统,这意味着很可能date
实际上是 GNU date
)。
awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null
这个东西会生成 100 行,例如
2017-08-01 + 22 days
2017-08-01 + 31 days
2017-08-01 + 11 days
2017-08-01 + 27 days
2017-08-01 + 27 days
2017-08-01 + 20 days
(etc.)
让我们将这些输入到 GNU 中date
。 GNUdate
有这个标志 ,-f
它允许我们批量输入多个日期规范,例如我们的awk
程序输出的日期规范:
awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null |
date -f - +'%Y-%m-%d'
现在我们得到
2017-08-23
2017-08-27
2017-08-21
2017-08-29
2017-08-25
2017-08-17
2017-08-07
(etc.)
然后只需将唯一 ID(一个连续整数)添加到每一行即可:
awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null |
date -f - +'%Y-%m-%d' |
awk -vOFS=',' '{ print NR, $0 }'
这给你
1,2017-08-06
2,2017-08-17
3,2017-08-25
4,2017-08-28
5,2017-08-14
6,2017-08-15
7,2017-08-17
8,2017-08-10
9,2017-08-16
10,2017-08-08
(etc.)
现在我们完成了。在这个过程中,我完全忘记了我们有一个 shell 循环。事实证明不需要。
只需将 调高100
到您想要的任何值,然后调整随机数生成器以满足您的需求。 rand()
返回一个浮点值,使得 0 <= number < 1。
显然,如果您只想要 8 月(有 31 天的月份)中的随机日期,您可以date
完全绕过:
awk 'END { for (i=1;i<=100;++i) { printf("%d,2017-08-%02d\n", i, 1+int(31*rand())) }}' /dev/null
使用 GNUawk
和 Mike 的awk
( mawk
),但不使用 BSD awk
,您甚至可以直接在 中进行正确的日期处理awk
:
awk 'END { for (i=1;i<=100;++i) { printf("%d,%s\n", i, strftime("%Y-%m-%d", 1501545600 + int(2678400*rand()),1 )) }}' /dev/null
现在我们处理的是 Unix 时间戳而不是天数。 1501545600 对应于“Tue Aug 1 00:00:00 UTC 2017”,31 天有 2678400 秒。
答案2
# A "random" date between 2000-01-01 and 2025-12-28
# Only uses day 01 to 28
rand_date() {
printf "%4d-%02d-%02d" $((RANDOM%25+2000)) $((RANDOM%12+1)) $((RANDOM%28+1))
}
csv_data() {
for ((i=1; i<="$1"; i++)); do printf "%d,%s\n" $i $(rand_date); done
}
$ time (csv_data 1000000 > data.csv)
real 7m26.683s
user 0m36.376s
sys 1m57.768s
Perl 可能更快,让我们尝试一下
$ cat data.pl
#!/usr/bin/perl
$, = ",";
$\ = "\n";
sub rand_date {
sprintf "%4d-%02d-%02d", int(rand(25))+2000, int(rand(12))+1, int(rand(28))+1;
}
sub csv_data {
my $n = shift;
for ($i = 1; $i <= $n; $i++) {
print $i, rand_date();
}
}
csv_data(1_000_000);
$ time (perl data.pl > data.csv)
real 0m0.881s
user 0m0.876s
sys 0m0.004s
嗯,是的,再快一点……