我正在尝试运行一个命令,该命令将读取目录中的多个文件,查看每个文件的每一行,每当它在任何行上读取特定字符串(“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