我正在尝试使用用户定义的行数和每行字符数的随机 0 和 1 序列填充文件。
第一步是获取 0 和 1 的随机流:
cat /dev/urandom | tr -dc 01
然后我尝试用这个流填充一个文件(并通过ctrl+c结束填充过程)
cat /dev/urandom | tr -dc 01 > foo
当我计算如此创建的 foo 文件的行数时,我得到 0 行。
cat foo | wc -l 0
现在我尝试控制流,因此我创建了一个命名管道并将流引导到其中。然后我用 dd 命令连接到命名管道,徒劳地希望通过这种方式控制每行的字符数和文件中的行数。
makefifo namedpipe
cat /dev/urandom | tr -dc 01 > namedpipe
dd if=namedpipe of=foo bs=10 count=5
foo 文件确实被 50 个字节的 0 和 1 填充,但行数仍然是 0。
我该如何解决这个问题,我想也许我必须将每个字符数插入一个换行符到文件中,但如果是这样,我不知道该怎么做。
答案1
怎么样fold
?它是 coreutils 的一部分...
$ tr -dc 01 < /dev/urandom | fold -w 30 | head -n 5
001010000111110001100101101101
000101110011011100100101111000
111010101011100101010110111001
111011000000000101111110110100
110011010111001110011010100011
或者,如果不可用,可以使用以下内容awk
:
$ tr -dc 01 < /dev/urandom | awk \$0=RT RS=.\{,30} | head -n 5
000100010010001110100110100111
101010010100100110111010001110
100011100101001010111101001111
010010100111100101101100010100
001101100000101001111011011000
或者你可以用循环做一些事情......
$ for line in $(seq 1 5)
> do
> echo $(tr -dc 01 < /dev/urandom | head -c 30)
> done
100101100111011110010010100000
000000010000010010110111101011
010000111110010010000000010100
001110110001111011101011001001
001010111011000111110001100110
我确信还有其他方法...我想也许具有自定义格式的 hexdump 可以做到这一点,但显然不是...;)
答案2
LC_ALL=C </dev/urandom \
tr '\0-\377' '[0*128][1*]' |
dd ibs=50 cbs=10 conv=unblock count=1
这将转换所有输入的 ascii 字节(这将是所有字节,因为LC_ALL=C
已指定)成均匀分布的 0 或 1 之一。\0
和之间的前 128 个字节\177
被转换为 0 和\200-\377
1 - 因此您可以使用所有输入字节,并且仍然输出仅包含 1 或 0 的随机排序序列。
你的选择是正确的dd
,但是你不需要设置你的bs=
块大小来获得5个11字节的输出行(10 +\n
行)一块。相反,您应该为输入字节块指定count=1
单个字节,然后将其分为 5 个大小的转换块,并通过在删除所有尾随空格后向每个转换块附加 ewline 来按 cbs 大小进行编辑read()
ibs=50
cbs=10
conv=unblock
\n
(你没有)。
所以我运行它并打印了:
1101001010
1100001001
1101110100
1011011000
1011110100
1+0 records in
0+1 records out
55 bytes (55 B) copied, 0.00176591 s, 31.1 kB/s
我还稍微提高了赌注,以显示一种方法和另一种方法之间的速度比较,并证明dd
如果您以占写入实用程序缓冲区的块因子读入,则从管道读取不是问题 -尺寸。所以我首先做了:
time (
LC_ALL=C </dev/urandom \
tr -dc 01 |
dd ibs=4k cbs=10 conv=unblock count=k|
grep \[^01])
...在标准输出上没有输出(所以grep
除了 0 或 1 之外没有其他匹配)并在 stderr 上显示以下内容:
1024+0 records in
9011+1 records out
4613735 bytes (4.6 MB) copied, 25.8898 s, 178 kB/s
( LC_ALL=C tr -dc 01 < /dev/urandom |\
dd ibs=4k cbs=10 conv=unblock count=k |...)\
0.80s user 25.42s system 101% cpu 25.921 total
上面的信息告诉我们管道花费了 25.5 秒等待系统调用。好的。但它还告诉我们,dd
读入 4096 字节大小的输入记录中的全部 1024 条完全地并且没有一个因提前read()
返回而被截断 - 这是因为tr
缓冲区在 4k 块上通过管道输出。
不管怎样,接下来是用另一种方式——或者在扩频上转换所有随机输入:
time (
LC_ALL=C </dev/urandom \
tr '\0-\377' '[0*128][1*]' |
dd ibs=4k cbs=10 conv=unblock count=k|
grep '[^01]')
再一次,标准输出上没有任何内容 - 所以全部ofdd
的输出是零或一或换行符之一 - 这在 stderr 上:
1024+0 records in
9011+1 records out
4613735 bytes (4.6 MB) copied, 0.554202 s, 8.3 MB/s
( LC_ALL=C tr '\0-\377' '[0*128][1*]' \
< /dev/urandom|dd ibs=4k cbs=10 ...)\
0.61s user 0.36s system 171% cpu 0.571 total
...这再次证明dd
读入所有 1024 个完整输入记录 + 0 个截断输入记录,但处理时间明显不同。实际上能够在这里并行工作,并且在单独的内核上共同使用的用户总时间比整个过程在不到 0.6 秒内完成所需的时间还要多tr
。dd
这样就快一点了。
答案3
要在生成过程中添加换行符,请执行以下操作:
{ process-without-terminating-newline ; echo ;} > outfile
要将其添加到现有文件中,请执行以下操作:
echo >> outfile
答案4
然后我尝试用这个流填充一个文件(并通过ctrl+c结束填充过程)
cat /dev/urandom | tr -dc 01 > foo
当我计算如此创建的 foo 文件的行数时,我得到 0 行。
cat foo | wc -l 0
两者cat
和tr
缓冲他们的输出。当您按Ctrl+时C,任一命令缓冲区中仍在的任何数据都会丢失。您很早就中断了程序,tr
尚未积累完整的缓冲区值,因此它没有写出任何内容。
在 Linux 上,您可以用于head
在一定字节数后截断数据。
i=0
while [ "$i" -lt "$number_of_lines" ]; do
</dev/urandom tr -dc 01 | head -c "$bits_per_line"; echo
i=$((i+1))
done >foo
或者,生成所需的字节数,并用于fold
注入换行符。
</dev/urandom tr -dc 01 |
fold -w "$bits_per_line" |
head -n "$number_of_lines"
拒绝每一个不是0
或1
相当慢的字节:你拒绝了 127/128 的输入。没有标准实用程序可以生成以 2 为基数的输出,但您可以使用它od
来生成十六进制并逐位转换。
</dev/urandom od -An -tx1 |
sed 's/ //g; s/0/@@@@/g; s/1/@@@`/g; s/2/@@`@/g; s/3/@@``/g; s/4/@`@@/g; s/5/@`@`/g; s/6/@``@/g; s/7/@```/g; s/8/`@@@/g; s/9/`@@`/g; s/[Aa]/`@`@/g; s/[Bb]/`@``/g; s/[Cc]/``@@/g; s/[Dd]/``@`/g; s/[Ee]/```@/g; s/[Ff]/````/g; y/@`/01/' |
fold -w "$bits_per_line" |
head -n "$number_of_lines"
如果有xxd
,您可以使用它将字节转换为以 2 为基数的表示形式。如果每行的位数是 8 的倍数,您甚至可以使用其-c
选项使其在需要时插入换行符并-l
使其在几行后停止。
</dev/urandom xxd -b -c "$bytes_per_line" -l "$((bytes_per_line * number_of_lines))" |
sed -e 's/ .*//' -e 's/.*://'