【问题标题】:Replace and increment letters and numbers with awk or sed用 awk 或 sed 替换和增加字母和数字
【发布时间】:2017-03-01 20:50:32
【问题描述】:

我有一个包含

的字符串
fastcgi_cache_path /var/run/nginx-cache15 levels=1:2 keys_zone=MYSITEP:100m inactive=60m;

此脚本的目标之一是根据在前一个文件中找到的值将 nginx-cache 增加两位数。为此,我使用了以下代码:

# Replace cache_path

PREV=$(ls -t /etc/nginx/sites-available | head -n1) #find the previous cache_path number
CACHE=$(grep fastcgi_cache_path $PREV | awk '{print $2}' |cut -d/ -f4) #take the string to change
SUB=$(echo $CACHE |sed "s/nginx-cache[0-9]*[0-9]/&@/g;:a {s/0@/1/g;s/1@/2/g;s/2@/3/g;s/3@/4/g;s/4@/5/g;s/5@/6/g;s/6@/7/g;s/7@/8/g;s/8@/9/g;s/9@/@0/g;t a};s/@/1/g") #increment number
sed -i "s/nginx-cache[0-9]*/$SUB/g" $SITENAME #replace number

也许不那么优雅,但它确实有效。

另一个目标是增加所有出现的 MYSITEx 的最后一个字母(在这种情况下,MYSITEP 应该变成 MYSITEQ,在 MYSITEP 等之后,一旦 MYSITEZ 到达,添加另一个字母,如 MYSITEAA, MYSITEAB

我的想法是这样的:

sed -i "s/MYSITEP[A-Z]*/MYSITEGG/g" $SITENAME

但它不能工作,因为 MYSITEGG 是一个静态值,不能使用。 如何计算最后一个字母,将其递增到下一个字母,一旦到达最后一个 Z 字母,再添加另一个字母?

谢谢!

【问题讨论】:

  • Perl 递增字母。我建议您需要perl -p -e 解决方案。如果您愿意,请添加标签。

标签: bash perl nginx awk sed


【解决方案1】:

Perl 的自动增量将按照您描述的方式对字母和数字起作用

我们也可以在处理您的 nginx-cache 增量时整理一下

我假设SITENAME 包含要修改的文件的名称?

看起来像这样。我必须将捕获 $1 分配给普通变量 $n 以增加它,因为 $1 是只读的

perl -i -pe 's/nginx-cache\K(\d+)/ ++($n = $1) /e; s/MYSITE\K(\w+)/ ++($n = $1) /e;' $SITENAME

如果你愿意,这可以在一次替换中完成,就像这样

perl -i -pe 's/(?:nginx-cache|MYSITE)\K(\w+)/ ++($n = $1) /ge' $SITENAME

【讨论】:

  • 哇!这是一个非常好的解决方案!一切都在一条线上!它就像一个魅力。非常感谢@Borodin!
【解决方案2】:

注意:下面的解决方案是不必要的复杂,因为正如Borodin's helpful answer 所展示的(以及@stevesliva 对暗示的问题的评论),Perl 直接支持通过将++ 运算符应用于包含字母(序列)的变量,以问题中描述的方式按字母顺序递增字母;例如:

$ perl -E '$letters = "ZZ"; say ++$letters'
AAA

下面的解决方案可能仍然很有趣,它作为一个注释展示了如何从 shell 中利用 Perl 的力量,展示了以下技术:

  • 使用s///e 来确定带有表达式的替换字符串。
  • 将字符串拆分为字符数组 (split //, "....")
  • 使用ordchr 函数获取字符的代码点,并将(n 个递增的)代码点转换回字符。
  • 字符串复制(x 操作员)
  • 数组索引和切片:
    • 获取数组的最后一个元素 ($chars[-1])
    • 获取数组中除最后一个元素之外的所有元素 (@chars[0..$#chars-1])

perl 解决方案(实际上是重新实现 ++ 可以直接执行的操作):

perl -pe 's/\bMYSITE\K([A-Z]+)/
  @chars = split qr(), $1; $chars[-1] eq "Z" ? 
    "A" x (1 + scalar @chars)
    :
    join "", @chars[0..$#chars-1], chr (1 + ord $chars[-1])
/e'  <<'EOF'
...=MYSITEP:...
...=MYSITEZP:...
...=MYSITEZZ:...
EOF

产量:

...=MYSITEQ:...    # P -> Q
...=MYSITEZQ:...   # ZP -> ZQ
...=MYSITEAAA:...  # ZZ -> AAA

您可以使用perl-i 选项将输入文件替换为结果
(perl -i -pe '...' "$SITENAME")。

正如 Borodin 的回答所表明的,仅使用 perl 解决问题中的所有任务并不难。

s 函数的 /e 选项允许使用 Perl 表达式 来确定替换字符串,从而实现复杂的替换:

  • $1 引用表达式中的当前MYSITE 后缀。

  • @chars = split qr(), $1 将后缀拆分为字符数组。

  • $chars[-1] eq "Z" 测试是否是最后一个后缀字符。是Z

    • 如果是这样:后缀被全部替换为As,并附加了一个A
      ("A" x (1 + scalar @chars))。

    • 否则:最后一个后缀字符。替换为字母表中的以下字母
      (join "", @chars[0..$#chars-1], chr (1 + ord $chars[-1]))

【讨论】:

  • 用单个 Awk 脚本替换长时间运行的 grep | sed | awk | sed 可能更容易在不学习新语言的情况下实现。一般来说,如果您使用的是 Awk,您应该非常少且非常少地在同一管道中需要 grepsed。但在这种特殊情况下,Perl 似乎很合适,因为它支持 OP 描述的开箱即用的行为。
  • 您还可以将.. 运算符与字母或零填充整数一起使用。 ('A'..'Z') 运算符中隐藏着很多字符串操作功能,您可能不会想到。
  • 感谢大家的出色解决方案!我试过@Borodin 的那个,效果很好!
猜你喜欢
  • 1970-01-01
  • 2012-12-30
  • 1970-01-01
  • 1970-01-01
  • 2016-10-30
  • 2017-11-11
  • 2012-06-24
  • 2014-12-04
  • 1970-01-01
相关资源
最近更新 更多