dd
可以重复写\0
字节非常快地写入文件,但它不能写入重复的任意字符串。
有没有bash shell任意重复写入的方法字符串与“dd”一样快(包括\0
)?
我在 6 个月的 Linux 中遇到的所有建议都是类似的printf "%${1}s" | sed -e "s/ /${2}/g"
,但这与 相比慢得令人痛苦dd
,如下所示,并且sed
在大约 384 MB(在我的盒子上)后崩溃——实际上这对于单行来说还不错——长度:)——但它确实崩溃了!我想如果字符串包含换行符,
这不会成为问题。sed
dd
与printf
+的速度比较sed
:
real user sys
WRITE 384 MB: 'dd' 0m03.833s 0m00.004s 0m00.548s
WRITE 384 MB: 'printf+sed' 1m39.551s 1m34.754s 0m02.968s
# the two commands used
dd if=/dev/zero bs=1024 count=$((1024*384))
printf "%$((1024*1024*384))s" |sed -e "s/ /x/g"
我有一个想法如何做到这一点bash shell脚本,但没有必要重新发明轮子。 :)
答案1
$ time perl -e \
'$count=1024*1024; while ($count>0) { print "x" x 384; $count--; }' > out
real 0m1.284s
user 0m0.316s
sys 0m0.961s
$ ls -lh out
-rw-r--r-- 1 me group 384M Apr 16 19:47 out
"x" x 384
将(生成 384 的字符串)替换x
为您喜欢的任何内容。
您可以通过在每个循环中使用更大的字符串并绕过正常的标准输出缓冲来进一步优化这一点。
$ perl -e \
'$count=384; while ($count>0) {
syswrite(STDOUT, "x" x (1024*1024), 1024*1024);
$count--;
}' > out
在这种情况下,syswrite
调用一次将 1M 传递给底层write
系统调用,这变得非常好。 (我用这个得到了大约 0.940 秒的用户。)
提示:确保sync
在每个测试之间进行调用,以避免前一次运行的刷新干扰当前运行的 I/O。
作为参考,我这次得到:
$ time dd if=/dev/zero bs=1024 count=$((1024*384)) of=./out
393216+0 records in
393216+0 records out
402653184 bytes (403 MB) copied, 1.41404 s, 285 MB/s
real 0m1.480s
user 0m0.054s
sys 0m1.410s
答案2
人们普遍认为 shell 处理大块数据的速度很慢。对于大多数脚本,您提前知道哪些数据位可能很小,哪些数据位可能很大。
- 更喜欢依赖 shell 内置函数来处理小数据,因为分叉和执行外部进程会产生持续的开销。
- 更喜欢依赖外部的专用工具来处理大数据,因为专用编译工具比解释型通用语言更有效。
dd
使用块大小的发出read
和调用。write
您可以使用 strace (或 truss、trace...取决于您的操作系统)观察这一点:
$ strace -s9 dd if=/dev/zero of=/dev/null ibs=1024k obs=2048k count=4
✄
read(0, "\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
read(0, "\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
write(1, "\0\0\0\0\0\0\0\0\0"..., 2097152) = 2097152
read(0, "\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
read(0, "\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
write(1, "\0\0\0\0\0\0\0\0\0"..., 2097152) = 2097152
✄
大多数其他工具的最大缓冲区大小的上限要低得多,因此它们会进行更多的系统调用,从而花费更多的时间。但请注意,这是一个不切实际的基准:如果您写入常规文件、管道或套接字,内核可能不会在每个系统调用中写入超过几千字节的数据。
答案3
你可以用dd
这个!首先将字符串写入文件的开头。然后做:
dd if=$FILE of=$FILE bs=$STRING_LENGTH seek=1 count=$REPEAT_TIMES
注意:如果你的 $STRING_LENGTH 很小,你可能会这样做
dd if=$FILE of=$FILE bs=$STRING_LENGTH seek=1 count=$((1024/$REPEAT_TIMES))
dd if=$FILE of=$FILE bs=1024 seek=1 count=$(($REPEAT_TIMES/1024))
(此示例仅在 STRING_LENGTH 是 2 的幂且 REPEAT_TIMES 是 1024 的倍数时有效,但您明白了)
如果您想使用它来覆盖文件(例如清除),请使用conv=notrunc
答案4
我终于有了关于如何完成这项工作的想法......它使用tee
| tee
|tee
链条,其运行速度接近dd
..
# ============================================================================
# repstr
#
# Brief:
# Make multiple (repeat) copies of a string.
# Option -e, --eval is used as in 'echo -e'
#
# Return:
# The resulting string is sent to stdout
#
# Args: Option $1 $2
# -e, --eval COUNT STRING
# repstr $((2**40)) "x" # 1 TB: xxxxxxxxx...
# eg. repstr -e 7 "AB\tC\n" # 7 lines: AB<TAB>C
# repstr 2 "ऑढळ|a" # 2 copies: ऑढळ|aऑढळ|a
#
[[ "$1" == "-e" || "$1" == "--eval" ]] && { e="-e"; shift 1; }|| e=""
count="$1"
string="$2"
[[ "${count}" == "" ]] && exit 1 # $count must be an integer
[[ "${count//[0-9]/}" != "" ]] && exit 2 # $count is not an integer
[[ "${count}" == "0" ]] && exit 0 # nothing to do
[[ "${string}" == "" ]] && exit 0 # nothing to do
#
# ========================================================================
# Find the highest 'power of 2' which, when calculated**, is <= count
# ie. check ascending 'powers of 2'
((leqXpo=0)) # Exponent which makes 2** <= count
((leqCnt=1)) # A count which is <= count
while ((count>=leqCnt)) ;do
((leqXpo+=1))
((leqCnt*=2))
done
((leqXpo-=1))
((leqCnt/=2))
#
# ======================================================================================
# Output $string to 'tee's which are daisy-chained in groups of descending 'powers of 2'
todo=$count
for ((xpo=leqXpo ;xpo>0 ;xpo--)) ;do
tchain=""
floor=$((2**xpo))
if ((todo>=(2**xpo))) ; then
for ((t=0 ;t<xpo ;t++)) ;do tchain="$tchain|tee -" ;done
eval echo -n $e \"'$string'\" $tchain # >/dev/null
((todo-=floor))
fi
done
if ((todo==1)) ;then
eval echo -n $e \"'$string'\" # >/dev/null
fi
#
这是一些时间测试结果..我已经达到了 32 GB,因为这大约是我想要创建的测试文件的大小(这就是我开始解决这个问题的原因)
NOTE: (2**30), etc. refers to the number of strings (to achieve a particular GB filesize)
-----
dd method (just for reference) real/user/sys
* 8GB =================================
if=/dev/zero bs=1024 count=$(((1024**2)*8)) # 2m46.941s / 00m3.828s / 0m56.864s
tee method: fewer tests, because it didn't overflow, and the number-of-strings:time ratio is linear
tee method: count string real/user/sys
* 8GB ========== ============ =================================
tee(2**33)>stdout $((2**33)) "x" # 1m50.605s / 0m01.496s / 0m27.774s
tee(2**30)>stdout -e $((2**30)) "xxx\txxx\n" # 1m49.055s / 0m01.560s / 0m27.750s
* 32GB
tee(2**35)>stdout -e $((2**35)) "x" #
tee(2**32)>stdout -e $((2**32)) "xxx\txxx\n" # 7m34.867s / 0m06.020s / 1m52.459s
python method: '.write' uses 'file.write()'
'>stcout' uses 'sys.stdout.write()'. It handles \n in args (but I know very little python)
count string real/user/sys
* 8GB ===== =================== =================================
python(2**33)a .write 2**33 "x" # OverflowError: repeated string is too long
python(2**33)a >stdout 2**33 "x" # OverflowError: repeated string is too long
python(2**30)b .write 2**30 '"xxxxxxxX" *2**0' # 6m52.576s / 6m32.325s / 0m19.701s
python(2**30)b >stdout 2**30 '"xxxxxxxX" *2**0' # 8m11.374s / 7m49.101s / 0m19.573s
python(2**30)c .write 2**20 '"xxxxxxxX" *2**10' # 2m14.693s / 0m03.464s / 0m22.585s
python(2**30)c >stdout 2**20 '"xxxxxxxX" *2**10' # 2m32.114s / 0m03.828s / 0m22.497s
python(2**30)d .write 2**10 '"xxxxxxxX" *2**20' # 2m16.495s / 0m00.024s / 0m12.029s
python(2**30)d >stdout 2**10 '"xxxxxxxX" *2**20' # 2m24.848s / 0m00.060s / 0m11.925s
python(2**30)e .write 2**0 '"xxxxxxxX" *2**30' # OverflowError: repeated string is too long
python(2**30)e >stdout 2**0 '"xxxxxxxX" *2**30' # OverflowError: repeated string is too long
* 32GB
python(2**32)f.write 2**12 '"xxxxxxxX" *2**20' # 7m58.608s / 0m00.160s / 0m48.703s
python(2**32)f>stdout 2**12 '"xxxxxxxX" *2**20' # 7m14.858s / 0m00.136s / 0m49.087s
perl method:
count string real / user / sys
* 8GB ===== =================== =================================
perl(2**33)a .syswrite> 2**33 "a" x 2**0 # Sloooooow! It would take 24 hours. I extrapolated after 1 hour.
perl(2**33)a >stdout 2**33 "a" x 2**0 # 31m46.405s / 31m13.925s / 0m22.745s
perl(2**30)b .syswrite> 2**30 "aaaaaaaA" x 2**0 # 100m41.394s / 11m11.846s / 89m27.175s
perl(2**30)b >stdout 2**30 "aaaaaaaA" x 2**0 # 4m15.553s / 3m54.615s / 0m19.949s
perl(2**30)c .syswrite> 2**20 "aaaaaaaA" x 2**10 # 1m47.996s / 0m10.941s / 0m15.017s
perl(2**30)c >stdout 2**20 "aaaaaaaA" x 2**10 # 1m47.608s / 0m12.237s / 0m23.761s
perl(2**30)d .syswrite> 2**10 "aaaaaaaA" x 2**20 # 1m52.062s / 0m10.373s / 0m13.253s
perl(2**30)d >stdout 2**10 "aaaaaaaA" x 2**20 # 1m48.499s / 0m13.361s / 0m22.197s
perl(2**30)e .syswrite> 2**0 "aaaaaaaA" x 2**30 # Out of memory during string extend at -e line 1.
perl(2**30)e >stdout 2**0 "aaaaaaaA" x 2**30 # Out of memory during string extend at -e line 1.
* 32GB
perl(2**32)f .syswrite> 2**12 "aaaaaaaA" x 2**20 # 7m34.241s / 0m41.447s / 0m51.727s
perl(2**32)f >stdout 2**12 "aaaaaaaA" x 2**20 # 10m58.444s / 0m53.771s / 1m28.498s