如何使用来自 /dev/urandom 的流填充指定行数的文件?

如何使用来自 /dev/urandom 的流填充指定行数的文件?

我正在尝试使用用户定义的行数和每行字符数的随机 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-\3771 - 因此您可以使用所有输入字节,并且仍然输出仅包含 1 或 0 的随机排序序列。

你的选择是正确的dd,但是你不需要设置你的bs=块大小来获得5个11字节的输出行(10 +\n行)一块。相反,您应该为输入字节块指定count=1单个字节,然后将其分为 5 个大小的转换块,并通过在删除所有尾随空格后向每个转换块附加 ewline 来按 cbs 大小进行编辑read()ibs=50cbs=10conv=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 秒内完成所需的时间还要多trdd这样就快一点了。

答案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

两者cattr 缓冲他们的输出。当您按Ctrl+时C,任一命令缓冲区中仍在的任何数据都会丢失。您很早就中断了程序,tr尚未积累完整的缓冲区值,因此它没有写出任何内容。

不要用于dd从字符设备或管道中读取

在 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"

拒绝每一个不是01相当慢的字节:你拒绝了 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/.*://'

相关内容