【问题标题】:How to increment the last number in a string; bash如何增加字符串中的最后一个数字;重击
【发布时间】:2019-06-19 09:52:24
【问题描述】:

我有一个字符串,看起来像 /foo/bar/baz59_ 5stuff.thing

如果最后一个数字(示例中为 5)大于另一个变量,我想将其加一。 9 将增加到 10。请注意,最后一个数字也可以是多位数字; “stuff.thing”也可以是任何东西;不是一个数字;所以它不能被硬编码。

上面的例子会导致/foo/bar/baz59_ 6stuff.thing

我发现了多个可以从字符串中提取最后一个数字的问题(和答案),显然可以将其用于比较。 我遇到的问题是如何确保当我进行替换时,我只替换最后一个数字(因为显然我不能将“5”替换为“6”)。谁能给点建议?

awk/sed/bash/grep 都是可行的。

【问题讨论】:

  • 如果最后一个数字是 9,那么期望的结果是什么?如果是 59 岁呢?
  • 如果最后一个数字是 9;那么想要的是 10. 59; 60. 99->100 等(为后代更新问题)
  • 你的最后一个数字总是以下划线开头?
  • @gboffi 没有;前一个字符不固定。
  • if it's greater than a script argument 你的意思是整个字符串,还是数字?如果是数字,您的意思是增加字符串中大于参数的最后一个数字,还是增加字符串中的最后一个数字,如果它大于参数?

标签: bash perl awk sed


【解决方案1】:

如果它大于脚本参数。

如果我理解正确(我假设您正在通过脚本传递一个参数,并且如果它的值大于字符串的第二个字段数字,那么将 1 增加到该第二个字段的数字),请您尝试一次。

cat script.ksh
value=$1
echo "/foo/bar/baz59_ 5stuff.thing" | 
awk -v arg="$value" '
  match($2,/[0-9]+/){
    val=substr($2,RSTART,RLENGTH)
    val=val<arg?val+1:val
    $2=val substr($2,RSTART+RLENGTH)
}
1'

这是一个示例,当我运行 script.ksh 时,它会给出以下输出。

/script.ksh 7
/foo/bar/baz59_ 6stuff.thing

【讨论】:

  • @UKMonkey,你能不能也检查一下这个,让我知道这是否适合你?
  • @UKMonkey,很高兴它对您有所帮助,为什么我特别要求没有其他解决方案从脚本中获取参数并进行比较,所以我想确保我正确理解了问题,如果您测试通过传递参数然后很酷:)
  • 这是我关心的替换背后的原则 - 完整的实现,例如如何插入字符串等是我不想感染问题的担心。
  • @UKMonkey,好吧(如果我理解正确的话)如果这是完整的要求那么你现在不用担心,你现在有完整的解决方案:)
【解决方案2】:

更新答案

感谢@EdMorton 指出超过阈值的数量的进一步要求。可以这样做:

perl -spe 's/(\d+)(?!.*\d+)/$1>$thresh? $1+1 : $1/e' <<<  "abc123_456.txt" -- -thresh=500

原答案

您可以在 Perl 正则表达式中使用 /e 评估/计算替换。这里我只是将 1 添加到捕获的数字字符串中,但您可以做更复杂的事情:

perl -pe 's/(\d+)(?!.*\d+)/$1+1/e' <<< "abc123_456.txt"
abc123_457.txt

(?!.*\d+) 是(希望)对任何更多数字的负前瞻。

$1 表示在捕获组(\d+) 中捕获的任何数字序列。

请注意,这需要修改以处理十进制数、负数和科学记数法 - 但这是可能的。

【讨论】:

  • 这似乎工作得很好;甚至处理包含引号的字符串
  • s/.*(?&lt;!\d)\K(\d+)/$1+1/sea
  • @EdMorton 很好发现!我没看过,现在加进去了。
  • s/(\d+)(?=\D*$)/$1+1/ea
  • 感谢大家的替代建议。
【解决方案3】:

使用bash正则表达式匹配:

$ f="/foo/bar/baz59_ 99stuff.thing"
$ [[ $f =~ ([0-9]+)([^0-9]+)$ ]]

好的,我们现在有什么?

$ declare -p BASH_REMATCH
declare -ar BASH_REMATCH=([0]="99stuff.thing" [1]="99" [2]="stuff.thing")

所以我们可以构造新的文件名

if [[ $f =~ ([0-9]+)([^0-9]+)$ ]]; then
    prefix=${f%${BASH_REMATCH[0]}}           # remove "99stuff.thing" from $f
    number=$(( 10#${BASH_REMATCH[1]} + 1 ))  # use "10#" to force base10
    new=${prefix}${number}${BASH_REMATCH[2]}
    echo $new
fi
# => /foo/bar/baz59_ 100stuff.thing

【讨论】:

    【解决方案4】:

    一个 awk:

    $ echo /foo/bar/baz59_ 99stuff.thing |
    awk '
    /[0-9]/ {
        rstart=1                                    # keep track of the start
        while(match(substr($0,rstart),/[0-9]+/)) {  # while numbers last
            rstart+=RSTART+RLENGTH-1                # increase rstart
            rlength=RLENGTH                         # remember length too
        }
        v=substr($0,rstart-rlength,rlength)+1                    # increase last number
        print substr($0,1,rstart-rlength-1) v substr($0,rstart)  # print in parts
        next
    }1'                                             # in case there was no number
    /foo/bar/baz59_ 100stuff.thing
    

    编辑

    哎呀,我错过了参数要求(如果它大于脚本参数,则将最后一个数字增加一):

    $ echo /foo/bar/baz59_ 99stuff.thing |
    awk -v arg=100 '
    /[0-9]/ {
        rstart=1
        while(match(substr($0,rstart),/[0-9]+/)) {
            rstart+=RSTART+RLENGTH-1
            rlength=RLENGTH
        }
        v=substr($0,rstart-rlength,rlength)
        if(0+v>arg) {                           # test if v greater that argument
            print substr($0,1,rstart-rlength-1) v+1 substr($0,rstart)
            next
        }
    }1'
    

    现在输出:

    /foo/bar/baz59_ 99stuff.thing
    

    【讨论】:

    • 更新了脚本参数要求。一开始错过了。
    【解决方案5】:

    这是一个较短的gnu awk 方法:

    cat incr.awk
    {
       n = split($0, a, /[0-9]+/, b)
       for(i=1; i<n; i++)
          s = s a[i] b[i] + (b[i] < max && i == n-1 ? 1 : 0)
       print s a[i]
    }
    

    然后将其用作:

    awk -v max=80 -f incr.awk <<< '/foo/bar/baz59_ 5stuff.thing'
    /foo/bar/baz59_ 6stuff.thing
    
    awk -v max=80 -f incr.awk <<< '/foo/bar/baz59_ 79stuff.thing'
    /foo/bar/baz59_ 80stuff.thing
    
    
    awk -v max=80 -f incr.awk <<< '/foo/bar/baz59_ 90stuff.thing'
    /foo/bar/baz59_ 90stuff.thing
    
    awk -v max=80 -f incr.awk <<< '/foo/bar/baz59_ 80stuff.thing'
    /foo/bar/baz59_ 80stuff.thing
    
    awk -v max=80 -f incr.awk <<< '/foo/bar/stuff.thing'
    /foo/bar/stuff.thing
    

    【讨论】:

      【解决方案6】:

      使用 GNU awk 将第三个参数匹配():

      $ awk -v t=3 'match($0,/(.*)([0-9]+)([^0-9]*)$/,a) && a[2]>t{a[2]++; $0=a[1] a[2] a[3]} 1' file
      /foo/bar/baz59_ 6stuff.thing
      

      只需将t 设置为用于递增的阈值,例如:

      $ awk -v t=7 'match($0,/(.*)([0-9]+)([^0-9]*)$/,a) && a[2]>t{a[2]++; $0=a[1] a[2] a[3]} 1' file
      /foo/bar/baz59_ 5stuff.thing
      

      【讨论】:

        【解决方案7】:

        如果“测试”号在“绑定”变量中:

        perl -pe 'BEGIN{$bound=6} s{(\d+)_(\d+)(?!.*\d+)}{ $i=$2+1;($i>$bound? $1+1:$1)."_".$i}e' <<<"/foo/bar/baz59_5stuff.thing"
        /foo/bar/baz59_6stuff.thing
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-05-03
          • 2015-06-18
          • 1970-01-01
          • 1970-01-01
          • 2020-12-23
          • 1970-01-01
          • 1970-01-01
          • 2020-03-12
          相关资源
          最近更新 更多