读取多个文件中的行时,sed 中的 $RANDOM 结果不同

读取多个文件中的行时,sed 中的 $RANDOM 结果不同

我正在尝试运行一个命令,该命令将读取目录中的多个文件,查看每个文件的每一行,每当它在任何行上读取特定字符串(“gene_height”)时,它都会将该行上的 2 个数字替换为 170- 220.例如以下内容:

        complexion={ "complexion_3" 60 "complexion_3" 132 }
        gene_height={ "normal_height" 192 "normal_height" 161 }

它应该看到“gene_height”,并将 192 和 161 替换为 170 到 220 之间的随机数,但它应该单独保留前一行中的 60 和 132,因为它不以“gene_height”开头。我目前拥有的几乎有效,问题是它对所有内容都使用相同的数字,因为它实际上每个文件只运行 $RANDOM 一次,而且我找不到将其拆分为每行工作的好方法。

目前我正在使用:

for f in *.txt; do sed -i -e '/\bgene_height\b/ s/ [0-9][0-9]*/ '$((169+RANDOM%51))'/g' $f; done

任何帮助表示赞赏!

答案1

尝试awk(不是 sed):

awk '/^gene_height/{
       $3=sprintf("%.0f",(169+rand()*5))
       $5=sprintf("%.0f",(169+rand()*5))
    }1' test*.txt

答案2

假设大括号内的两个值始终位于 string 之后"normal_height",并且大括号内没有其他内容:

(顺便说一句,有"normal_height"两次对我来说似乎很奇怪,但这就是您的示例文本中的内容,所以这就是我要使用的)。

perl -pe 'BEGIN{ $gh_fmt = "gene_height={ \"normal_height\" %i  \"normal_height\" %i }" };
          s/gene_height\h*=\h*\{[^}]*\}/sprintf($gh_fmt, int(rand(51)+170), int(rand(51)+170))/e
          ' input.txt 

示例输出:

                complexion={ "complexion_3" 60 "complexion_3" 132 }
                geneheight={ "normal_height" 216  "normal_height" 202 }

笔记:

  • -p选项导致perl操作类似于sed- 即读取输入的每一行,将代码应用于每一行,然后打印该行(无论是否修改)。

  • BEGIN {}代码块在执行其他任何内容之前仅执行一次。其余代码在隐式 while-read 类型循环中针对每个输入行执行一次。

  • 在任何包含 的输入行上geneheight={,它将用所需的文本替换大括号之间的该行部分。它不会更改该行中的任何其他内容 - 即该行是否缩进一个空格、一个制表符或一个空格和两个制表符并不重要。是否缩进,或者在模式之前或之后的同一行上是否有其他文本都无关紧要gene_height={...}

  • \{[^}]*\}正则表达式模式的部分与文字字符{、零个或多个字符匹配不是a },后跟一个文字}字符。即整个{...}字符串。

  • 搜索模式使用\h*(零个或多个水平空白字符)来处理=符号周围的任何可选空白。它将按原样与您的示例输入一起工作,并且如果输入包含gene_height = {或类似内容,它也将工作。输出将始终与$gh_fmt字符串一致(即没有可选的空格)。

  • 注意:对于这个一次只读取一行的特定脚本, (水平空白)的工作方式与(任何空白,包括换行符和换页符等)\h完全相同。\s然而,Perl 能够处理多行输入,因此无论如何都值得记住这种区别。查看man perlre并搜索"Pattern White Space".

    一次吸收整个文件并全局应用该操作的脚本版本s///如下所示:

    perl -0777 -pe '
       BEGIN{ $gh_fmt = "geneheight={ \"normal_height\" %i  \"normal_height\" %i }" };
       s/gene_height\s*=\s*\{[^}]*\}/sprintf($gh_fmt, int(rand(51)+170), int(rand(51)+170))/mge
       ' input.txt
    

    该版本能够处理包含任何类型空白的输入,包括换行符、符号之前或之后=,甚至{...}字符串内。请注意添加了m多行字符串的修饰符和g全局匹配的修饰符(即匹配输入中的所有匹配项)

  • 使用perl 的搜索和替换运算e符修饰符。s///它导致右侧 (RHS)(替换字符串)被评估为 perl 代码。即它运行该sprintf()函数。查看man perlop并搜索"s/PATTERN/REPLACEMENT/msixpodualngcer"

  • 我可以将格式字符串直接放入sprintf()函数中。相反,我选择使用变量($gh_fmt在 BEGIN 块中定义),因为在我看来,它使其更具可读性。它还使得如何扩展脚本以处理更多模式(只需添加更多格式字符串和更多s///操作)变得更加明显。

  • int(rand(51))返回 0 到 50 之间的随机数。将其与 170 相加,得到 170 到 220 之间的随机数perldoc -f rand。请参阅 。

  • 该脚本将使用标准输入和/或一个或多个输入文件。

  • 您可以使用 Perl 的-i就地编辑选项使其更改输入文件而不是打印到标准输出。man perlrun有关 -i 如何工作的详细信息,请参阅参考资料。

答案3

使用 GNU awk (我假设你有,因为你显然在使用 GNU sed 给定你的问题的脚本)作为第三个参数match()\<单词边界:

$ cat tst.awk
BEGIN { min=170; max=220; range=max-min+1; srand() }
match($0,/(.*\<gene_height=.* )[0-9]+(.* )[0-9]+(.*)/,a) {
    x = int( min + rand()*range )
    y = int( min + rand()*range )
    $0 = a[1] x a[2] y a[3]
}
{ print }

$ awk -f tst.awk file
        complexion={ "complexion_3" 60 "complexion_3" 132 }
        gene_height={ "normal_height" 198 "normal_height" 183 }

$ awk -f tst.awk file
        complexion={ "complexion_3" 60 "complexion_3" 132 }
        gene_height={ "normal_height" 197 "normal_height" 205 }

如果您想进行“就地”编辑,只需添加-i inplace,当然您不需要将脚本存储在文件中,因此您可以执行以下操作:

awk -i inplace '
    BEGIN { min=170; max=220; range=max-min+1; srand() }
    match($0,/(.*\<gene_height=.* )[0-9]+(.* )[0-9]+(.*)/,a) {
        x = int( min + rand()*range )
        y = int( min + rand()*range )
        $0 = a[1] x a[2] y a[3]
    }
    { print }
' *.txt

相关内容