【问题标题】:Create a file of a specific size with random printable strings in bash在 bash 中使用随机可打印字符串创建特定大小的文件
【发布时间】:2017-04-17 01:03:52
【问题描述】:

我想创建一个特定大小的文件,只包含 bash 中的可打印字符串。

我的第一个想法是使用/dev/urandom:

dd if=/dev/urandom of=/tmp/file bs=1M count=100
  100+0 records in
  100+0 records out
  104857600 bytes (105 MB, 100 MiB) copied, 10,3641 s, 10,1 MB/s

file /tmp/file && du -h /tmp/file
  /tmp/file: data
  101M  /tmp/file

这给我留下了一个我想要大小的文件,但不仅包含可打印的字符串。

现在,我可以使用strings 创建一个只包含可打印字符串的文件。

cat /tmp/file | strings > /tmp/file.txt
file /tmp/file.txt && du -h /tmp/file.txt 
  /tmp/file.txt: ASCII text
  7,0M  /tmp/file.txt

这给我留下了一个只包含可打印字符串但文件大小错误的文件。

TL;DR

如何在 bash 中创建一个特定大小的文件,只包含可打印的字符串?

【问题讨论】:

    标签: string bash random


    【解决方案1】:

    正确的方法是使用base64之类的转换将随机字节转换为字符。这不会消除源中的任何随机性,只会将其转换为其他形式。
    对于 1 兆字节大小的(稍大一点的)文件:

    dd if=/dev/urandom bs=786438 count=1 | base64 > /tmp/file
    

    生成的文件将包含A–Za–z0–9 和+/= 范围内的字符。

    下面是文件变大一点的原因,以及解决办法。

    您可以使用 tr 添加一个过滤器,将该列表转换为其他一些列表(大小相同或更小)。

    cat /tmp/file | tr 'A-Za-z0-9+/=' 'a-z0-9A-Z$%'
    

    我将= 留在了翻译之外,因为为了获得均匀的随机分布,最好省略最后一个(几乎)总是为= 的字符。

    尺寸

    文件的大小将从/dev/random 中使用的原始大小扩大4/3。那是因为我们将 256 个字节值转换为 64 个不同的字符。这是通过从字节流中提取 6 位来编码每个字符来完成的。当 4 个字符被编码(6*4=24 位)时,仅消耗了三个字节(8*3=24)。

    因此,我们需要字节数为 3 的倍数才能得到准确的结果,并且需要 4 的倍数,因为我们必须除以它。
    我们无法获得恰好 1024 字节 (1k) 或 1024*1024 = 1,048,576 字节 (1M) 的随机文件,因为两者都不是 3 的精确倍数。但是我们可以生成一个稍大一点的文件并将其截断(如果这样的精度是需要):

    wanted_size=$((1024*1024))
    file_size=$(( ((wanted_size/12)+1)*12 ))
    read_size=$((file_size*3/4))
    
    echo "wanted=$wanted_size file=$file_size read=$read_size"
    
    dd if=/dev/urandom bs=$read_size count=1 | base64 > /tmp/file
    
    truncate -s "$wanted_size" /tmp/file 
    

    截断为精确值的最后一步是可选的。

    随机生成。

    由于你要从 urandom 中提取这么多随机值,所以请不要使用random(使用 urandom),否则你的应用会被长时间阻塞,计算机的其余部分将在没有随机性的情况下运行。

    我会建议你安装包有:

    haveged 使用 HAVEGE(硬件易失熵收集和扩展) 维护一个 1M 的随机字节池,用于填充 /dev/random 每当 dev/random 中的随机位供应低于低 设备的水印。

    如果可能的话。

    【讨论】:

    • @farhad 好的,答案已经完成,请查看。
    【解决方案2】:

    @MarekNowaczyk 的转换 回答简单的 bash:

    #!/bin/sh
    (( $# )) || {  echo "Pass file size as initial parameter" >&2; exit 1; }
    size=$1
    mk_range(){ name=$1; shift; printf -v "$name" '%b' "$(printf '\\U%08x' "$@")"; }
    add_chars(){ local var; mk_range var "$@"; chars+=$var; }
        ## uncomment following lines to use each range.
        add_chars {48..57}    # 0-9 numbers
        add_chars {65..90}    # A-Z LETTERS
        add_chars {97..122}   # a-z letters
        add_chars {32,{33..47},{58..64},{91..96},{123..127}}     # other chars.
        # convert list of characters to an array of characters.
        [[ $chars =~ ${chars//?/(.)} ]] && arr=("${BASH_REMATCH[@]:1}");
        alphabet_len=${#arr[@]} 
        # loop to print random characters
        for ((i=0;i<$size;i++)); do
            idx=$((RANDOM%alphabet_len))
            printf '%s' "${arr[idx]}"
        done
        # Add a trailing new line.
        echo
    

    此代码不保证生成的随机分布是均匀的,它是作为示例编写的。为了确保输出中的随机分布, 我们将不得不使用谨慎的任意精度算法来更改基数(输出字符数)。
    此外,RANDOM 不是 CSPRNG。

    【讨论】:

    • 不错的一个:),但不幸的是它在 Win (MINGW) 上不起作用。该环境中的 bash 语法存在一些问题,默认情况下没有 random 工具。
    【解决方案3】:

    这个怎么样?

    size=1048576 # 1MB
    fname="strings.txt"
    
    while read line ; do
        # Append strings to the file ...
        strings <<< "${line}" >> "${fname}"
        fsize="$(du -b "${fname}" | awk '{print $1}')"
        # ... until it is bigger than the desired size
        if [ ${fsize} -gt ${size} ] ; then
            # Now truncate the file to the desired size and exit the loop
            truncate -s "${size}" strings.txt
            break
        fi 
    done < /dev/urandom
    

    我承认它不是很有效。我更快的尝试是使用dd:

    size=1048576
    fname="strings.txt"
    
    truncate -s0 "${fname}"
    
    while true ; do
        dd if=/dev/urandom bs="${size}" count=1 | strings >> "${fname}"
        fsize="$(du -b "${fname}" | awk '{print $1}')"
        if [ ${fsize} -gt ${size} ] ; then
            truncate -s "${size}" strings.txt
            break
        fi
    done
    

    【讨论】:

    • +1 虽然这可以完成工作,但效率很低,因为命令strings 会拒绝很多字节。如果所有字节都可以通过一些转换在输出中使用会更好。请阅读我的回答。
    • 当然,但是base64 降低了随机性。但我不确定这在这里是否重要。我要提出的另一点是,您只创建一个大字符串而不是多个随机字符串。我理解这样的问题
    • 不,base64 保持随机性。就像写 0x83 或十进制 131,一个使用两位数,其他三位,但都表示相同的数字。像二进制10000011 也代表十进制131。只是相同值的不同表示,完全无损失的转换。我相信,不要浪费时间或精力来计算将被丢弃的东西总是相关的。
    • 字符串命令将选择文件中看起来像文本的字符,这与要求带有换行符的字符串不同。但是可以通过使用带有 tr 的(也是无损的)转换来轻松地将其添加到脚本中,以在打印的范围内包含一个换行符。
    • 事实上,strings 命令只会选择由 4 个以上连续可打印字符组成的字符串,而将其余的排除在外。
    【解决方案4】:

    你可以用 awk 的方式来做,自定义字符集。

    此解决方案专用于 Windows bash 用户 - MINGW,因为默认 MINGW 环境中没有 dd、random 工具。

    random_readable.sh 从定义的字母表中随机化 N 个字符的 Bash 脚本:

    #!/bin/sh
    
    if [ -z $1 ]; then
        echo "Pass file size as initial parameter"
        exit
    fi
    
    SIZE=$1
    seed=$( date +%s )
    
    awk -v size="$SIZE" -v seed="$seed" '
    # add characters from range (a .. b) to alphabet
    function add_range(a,b){
        idx=a;
        while (idx <= b) {
            alphabet[idx] = sprintf("%c",idx)
            idx+=1
        }
    }
    BEGIN{
        srand(seed);  
        NUM=size;  
        idx=0;  
    
        # creating alfphabet dictionary
        add_range(32,126)   # all printable
        ## uncomment following lines to random [a-zA-Z0-9<operators>]
        # add_range(48,57)    # numbers
        # add_range(65,90)    # LETTERS
        # add_range(97,122)   # letters
        # add_range(33,47)    # operators: !"# .. etc
    
        # alfphabet to alphanums array
        idx=0
        for (k in alphabet){
            alphanums[idx]=alphabet[k]
            idx+=1
        }
        alphabet_len = idx
        i=0
    
        # and iterate to random some characters
        idx =0
        while (idx < NUM){                         
            dec =0
            char_idx=int(rand() * alphabet_len)
            char = alphanums[char_idx]
            printf("%s",alphanums[char_idx])
            idx+=1
        }  
    }  
    ' 
    

    创建文件:

    random_readable.sh 100 > output.txt
    

    【讨论】:

    • 我猜你错过了打印换行符,对吧?
    • hek2mgl - 不,我没有,因为目的是将该脚本的输出保存到文件中。使用换行符,文件会超大 1 个字节。
    • 好的,当然。但是您可以在字符列表中简单地使用换行符而不是空格。但这无关紧要。好答案
    • 只是为了好玩,我converted your script to plain bash。
    【解决方案5】:

    您可以使用以下方法之一:

    1. truncate 你应该有一个比你需要的更大的基线文本文件。 然后使用以下内容:

      truncate -s 5M filename
      DESCRIPTION
         Shrink or extend the size of each FILE to the specified size
      
      [...]
      
       -s, --size=SIZE
            set or adjust the file size by SIZE
      

    2.使用tail:此选项也需要参考文本文件。

    tail -c 1MB reference_big.txt> 1mb.txt
    

    【讨论】:

    • 这意味着我必须考虑到问题中提到的文件大小,首先创建一个大约 1.45GB 的随机非字符串文件,对吗?
    • 1.45GB的随机字符串文件
    【解决方案6】:

    又快又脏解决方案

    根据您的要求,在urandom 上使用string

    dd if=<(strings </dev/urandom) bs=4K count=25600 of=/tmp/file
    

    .. 甚至

    dd bs=4K count=25600 if=/dev/urandom |
        tr \\000-\\037\\200-\\377 \\040-\\077\\040-\\077\\040-\\140 >/tmp/file
    

    【讨论】:

    • 我喜欢这样。我什至不知道if=&lt;(strings &lt;/dev/urandom) 是可能的!
    • 第一,你必须生成更多随机和垃圾非 ascii,第二会将所有字节转换为可打印字符。
    【解决方案7】:

    你可以试试:

     cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1 > filename.txt
    

    与 tr -dc 'a-zA-Z0-9' 是字符集 fold -w 32 是长度和 head -n 1 是行数

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-22
      • 1970-01-01
      • 2023-03-17
      • 2018-03-16
      • 2016-08-19
      • 1970-01-01
      • 2019-06-02
      • 1970-01-01
      相关资源
      最近更新 更多