我想为一些实验生成一些大文件。
这是我的剧本。它创建一个文件,然后将其读取到一个变量中,然后尝试将其写入文件循环中定义的次数:
#! /usr/bin/env bash
set -e
set -u
< /dev/urandom tr -dc "\t\n [:alnum:]" | head -c32768 > temp.txt
data=$(cat ./temp.txt)
for testdir in "$@"; do
echo "create directory '$testdir'"
mkdir -p $testdir
for i in {1..3}; do
counter=$(printf %02d $i)
testfile=$testdir/test_${testdir##*/}_$counter.txt
echo "create file '$testfile'"
echo "$data" > $testfile
done
done
如果我尝试使用此脚本创建 3000 个文件(每个文件夹将有 3 个文件),则在我的系统上大约需要 19 秒:
时间generateUserData.sh测试{0..1000}
create directory 'TEST999'
create file 'TEST999/test_TEST999_01.txt'
create file 'TEST999/test_TEST999_02.txt'
create file 'TEST999/test_TEST999_03.txt'
create directory 'TEST1000'
create file 'TEST1000/test_TEST1000_01.txt'
create file 'TEST1000/test_TEST1000_02.txt'
create file 'TEST1000/test_TEST1000_03.txt'
real 0m19.333s
user 0m14.791s
sys 0m4.784s
我发现echo
这里可能是缓慢的部分。有什么想法如何让他尽快完成吗?
答案1
速度慢的宁愿是分叉进程并执行外部命令,例如mkdir
counter=$(printf %02d $i)
还在 bash 中 fork 一个进程。可以通过将其写为以下方式来避免这种情况:
printf -v counter %02d "$i"
或者:
printf -v testfile %s/%s_%02d.txt "$testdir" "${testdir##*/}" "$i"
mkdir
通过一次调用(mkdir -p -- "$@"
;不要忘记)创建所有目录,--
而不是mkdir
每个文件运行一个。
也不需要临时文件:
data=$(< /dev/urandom tr -dc "\t\n [:alnum:]" | head -c32768; echo .)
data=${data%.}
.
如果您希望$data
保证包含 32768 字节,因为命令替换会删除,则添加是必要的全部尾随换行符。另请注意,echo
不-n
加一回。对于任意数据printf
应该使用而不是无论如何:echo
另请注意,它head -c 32768
为您提供 32768 个字节,而不是字符,因此可能会在中间剪切字符。
printf %s "$data" > "$file"
答案2
承担一些什么斯蒂芬·查泽拉斯他们在很好的回答中说道,并进行了一些调整。
#!/usr/bin/env bash
set -e
set -u
main() {
< /dev/urandom tr -dc "\t\n [:alnum:]" | dd iflag=fullblock of=./temp.txt bs=32K count=1
mkdir -p -- "${@:?}"
for testdir in "$@"; do
for i in {1..3}; do
printf "%s/%s_%02d.txt\n" "$testdir" "${testdir##*/}" "$i"
done
done | xargs -n1 -P${proc:-16} cp ./temp.txt
}
time main "${@}"
dd
- 获取确切字节数的替代方法(尽管这只发生一次,所以无论哪种方式都不会产生太大差异)- 所有这些
echo
在我结束时增加了大约 3 秒,计数为 1000 - 多线程,可在运行时调整(通过
proc
变量)——尝试找到适合您系统的最佳值
例如
proc=32 bash ./foo.sh {1..1000}
注意——假设你最初关于填充变量的问题是一个实例XY问题... 如果说是这是一个硬性要求,我的答案按书面形式无效。
但这个改变应该做到这一点:
data="$(< /dev/urandom tr -dc "\t\n [:alnum:]" | dd iflag=fullblock bs=32K count=1)"
dd iflag=fullblock bs=32K count=1 of=./temp.txt <<<"${data}"
第二个dd
是确保我们只得到生成的数据,没有它,在两者之间的某个地方$( )
,<<<
我们似乎从某个地方找到了一个额外的字节(隐含的换行符?)。很高兴对此进行更正。我承认看起来有点麻烦,如果随机数据的大小是任意的或其他不重要的,我相信你可以简化它