时间generateUserData.sh测试{0..1000}

时间generateUserData.sh测试{0..1000}

我想为一些实验生成一些大文件。

这是我的剧本。它创建一个文件,然后将其读取到一个变量中,然后尝试将其写入文件循环中定义的次数:

#! /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是确保我们只得到生成的数据,没有它,在两者之间的某个地方$( )<<<我们似乎从某个地方找到了一个额外的字节(隐含的换行符?)。很高兴对此进行更正。我承认看起来有点麻烦,如果随机数据的大小是任意的或其他不重要的,我相信你可以简化它

相关内容