如何从一行中提取数字并将其保存到变量中?

如何从一行中提取数字并将其保存到变量中?

我觉得这是一个非常简单的问题,当我谷歌时,我找到了部分问题的很多答案,但是当我尝试将它们放在一起时,它不起作用,我不明白为什么。

这是场景:

  1. 我有一个文件,里面有很多文本。
  2. 其中一行与此模式匹配:foo = 1700;
  3. 我想提取1700
  4. 我想将其保存到 bash 脚本变量中,以便稍后在脚本中引用它。

我无法通过第 3 步。这是我尝试过的:

sed -nE 's/^foo = //p' file | sed -nE 's/;//p'

这打印出:

1700

很好,但是如果我需要修剪空白或其他内容怎么办?如果我不能使用*/ +,我就不知道该怎么做。我了解到你不能使用*/+替换另一个答案,所以我不知道如何做到这一点。我查看了 grep 的手册页,当我搜索该词时,我没有看到任何组选项。我想我知道如何在 awk 中解决这个问题,但我总是发现它的正则表达式函数有点笨拙,并且命令行脚本需要太多转义,所以理想情况下这不是解决这个问题的唯一方法。

答案1

  1. 首先,介绍如何捕获数值:

    $ echo 'foo = 1700;' | sed -n -e 's/^foo = \([0-9]\+\).*/\1/p'
    1700
    

    这是使用sed默认的基本正则表达式(BRE)。您还可以将扩展正则表达式 (ERE) 与 sed-E选项一起使用:

    echo 'foo = 1700;' | sed -n -E -e 's/^foo = ([0-9]+).*/\1/p'
    1700
    

    [0-9]+括号内的子表达式(...)捕获一个或多个数字。这称为“捕获组”,用于替换为\1(这是第一的捕获组 - 如果有多个捕获组,它们可以用作 \1、\2、\3 等)。

    在这种情况下,sed 脚本尝试仅使用 \1 捕获组替换整行,如果成功,则打印修改后的行。

  2. 接下来,您希望将sed的输出放入变量中。你这样做与命令替换。例如

    $ myvar=$(echo 'foo = 1700;' | sed -n -E -e 's/^foo = ([0-9]+).*/\1/p')
    $ echo $myvar
    1700
    
  3. 要在脚本中使用它,只需使用您的文件作为 sed 的参数,而不是通过管道输入echo ...它。

    myvar=$(sed -n -E -e 's/^foo = ([0-9]+).*/\1/p' file)
    
  4. 修剪空白,或处理可能具有可选前导空白或周围可选空白=等的行:

    myvar=$(sed -n -E -e 's/^[[:space:]]*foo[[:space:]]*=[[:space:]]*([0-9]+).*/\1/p' file)
    

    请注意,某些版本的 sed(至少是 GNU sed,也许其他版本)理解perl's \s,因此您可以将其缩短为:

     myvar=$(sed -n -E -e 's/^\s*foo\s*=\s*([0-9]+).*/\1/p' file)
    

答案2

为了完整起见,使用支持和的正则表达式的grep实现,您可以执行以下操作:-operl-P

grep -Po 'foo\s*=\s*\K\d+'

在哪里:

  • \s匹配任何空白字符
  • *0 个或多个前面的原子。例如\s*匹配 0 个或多个空白字符。
  • \d匹配十进制数字(通常与 相同[0123456789],但[0-9]通常不匹配更多字符)。
  • +匹配一个或多个前面的原子。
  • \K重置匹配部分的开始(要 eep 的内容,或者在要输出K的情况下)。grep -oo

因此,这将打印一个或多个数字的所有序列,后跟foo=两侧允许的任意数量的空格=,即使给定行上出现多个数字。

使用pcregrep,您还可以在后面指定一个数字-o来打印给定捕获组匹配的内容,而不是整个匹配部分:

pcregrep -o1 'foo\s*=\s*(\d+)'

可移植的是,您实际上可以使用真实的东西perl::

perl -lne 'print $1 for m{foo\s*=\s*(\d+)}g'

答案3

假设你想挑选一个foo数值,

echo 'foo = 1700;' | awk '$1=="foo" {print $NF+0}'
1700

默认情况下awk,按空格拆分(而不仅仅是单个空格)。NF是字段数,在本例中为 3;$NF是第 3 个空格字段的字符串值。+0将此字符串转换1700;为数值1700

它适用于诸如 之类的行,foo = 1700;但不适用于诸如 之类的行foo=1700;。从你的问题中我不确定你是否只是关心删除额外的空白,或者你的数据可能没有空白,而 和=;唯一的边界点。如果你想忽略任何空格,无论有没有,你最好使用sed,

echo 'foo=1700;' | sed -n 's/^foo *= *//p' | sed -e 's/;$//' -e 's/ *$//'
1700

答案4

awk '{for(i=1;i<=NF;i++){if($i ~ /foo/ && $0 ~ /foo.*=.*[0-9]*/){gsub(";","",$(i+2));print $(i+2)}}}' filename

测试并运行良好

相关内容