【问题标题】:How can I increment an infix variable in Bash?如何在 Bash 中增加中缀变量?
【发布时间】:2019-07-31 14:00:59
【问题描述】:

我有一个字符串foo-0,我想将其转换为bar1baz,即解析尾随索引并添加前缀/后缀。尾随索引之前的部分(在这种情况下 foo- 也可以包含数字字符,但不应更改。

我尝试了以下方法:

echo foo-0 | cut -d'-' -f 2 | sed 's/.*/bar&baz/'

但这只能给我一个部分解决方案 (bar0baz)。如何增加中缀变量?

编辑:以下解决方案仅部分适用于我想要实现的目标。这是我的错,因为为了清楚起见,我将上面的示例简化了太多。

最终目标是使用 bash 使用以下语法将环境变量(我们称之为 MY_ENV)设置为输出值:

/bin/sh -c "echo $var | ... (some bash magic to replace the trailing index) | ... (some bash magic to set MY_ENV=the output of the pipe)"

旁注:我使用 /bin/sh -c "..." 的原因是因为我想在 Kubernetes YAML 中使用该命令。

部分解决方案(使用awk

这行得通:

echo foo-0 | awk -F- '{print "bar" $2+1 "baz"}'

这不是(输出是1baz):

/bin/sh -c "echo foo-0 | awk -F- '{print \"bar\" $2+1 \"baz\"}'

部分解(使用算术上下文和参数扩展)

$ var=foo-0
$ echo "bar$((${var//[![:digit:]]}+1))baz"

如果var 在尾随索引之前包含其他数字字符(例如var foo=r2a-foo-0),这将不起作用。

【问题讨论】:

  • 如果在子shell中设置环境变量,不会影响父环境。

标签: bash awk pipe


【解决方案1】:

您可以使用awk:

awk -F- '{print "bar" $2+1 "baz"}' <<< 'foo-0'

bar1baz

【讨论】:

  • 这在使用/bin/sh -c "echo 'foo-1' | awk -F- '{print \"bar\" $2+1 \"baz\"}'" 时不起作用(输出为1baz)。我更新了问题,详细说明了我需要解决方案的用途。
  • 您需要使用:/bin/sh -c "echo 'foo-1' | awk -F- '{print \"bar\" \$2+1 \"baz\"}'" 和转义的$ 以避免调用 shell 时的变量扩展。
  • 我将此答案标记为正确,因为它为原始问题提供了最简单的解决方案。
【解决方案2】:

您可以使用算术上下文和参数扩展:

$ var=foo-0
$ echo "bar$((${var//[![:digit:]]}+1))baz"
bar1baz

从内部展开:

  • ${var//[![:digit:]]}var 中删除所有非数字:

    $ echo "${var//[![:digit:]]}"
    0
    
  • $((blah+1))变量blah加1:

    $ blah=0
    $ echo "$((blah+1))"
    1
    

    或者,我们可以使用内部替换的结果来代替blah

    $ echo "$(( ${var//[![:digit:]]} + 1 ))"
    1
    
  • 最后,把它放在barbaz 之间,你会得到bar1baz

修正另一种情况:假设可能有其他数字,我们只想增加尾随的数字,例如,

var=2a-foo-21

为此,我们可以使用带有扩展全局的嵌套参数扩展 (shopt -s extglob) 和匹配一个或多个 pattern+(<i>pattern</i>) 模式。观察:

$ echo "${var#"${var%%+([[:digit:]])}"}"
21

外扩展为${var#<i>pattern</i>},即从$var的开头删除pattern的最短匹配。对于pattern,我们使用

"${var%%+([[:digit:]])}"

这是“从$var 的末尾删除最长的+([[:digit:]])(一位或多位数字)匹配”。这让我们只剩下尾随数字,并在前后增加它们并添加字符串看起来像这样:

$ echo "bar$((${var#"${var%%+([[:digit:]])}"}+1))baz"
bar22baz

这太难读了,我建议改用正则表达式:

$ re='([[:digit:]]+)$'
$ [[ $var =~ $re ]]
$ echo "bar$((${BASH_REMATCH[1]}+1))baz"
bar22baz

【讨论】:

  • var 在尾随索引之前包含其他数字字符(例如r2a-foo-0)时,这不起作用。这是一个先决条件。我相应地更新了问题。
  • @tiefenauer 我添加了另一个考虑到这一点的解决方案。请注意,通常不赞成修改问题以使现有答案无效。
猜你喜欢
  • 2014-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-14
  • 2019-09-03
  • 1970-01-01
  • 2011-11-20
  • 2014-11-17
相关资源
最近更新 更多