给定一个字符串 s
s="B /home/BL/004_010_0100.0 23 0.031"
如何只 grep 字符串中路径、制表符和空格后面的数字?
在上面的字符串 s 中,我想提取数字 23。
num=$(echo $s | grep 'B .*\t (\d*)')
答案1
将字符串视为一组空格分隔的字段,您需要倒数第二个字段:
num=$( awk '{ print $(NF-1) }' <<<"$s" )
或者,在没有此处字符串的 shell 中,
num=$( printf '%s\n' "$s" | awk '{ print $(NF-1) }' )
这会将字符串输入$s
到awk
命令中。该awk
命令输出倒数第二个以空格分隔的字段。该结果被分配给num
变量。
测试:
$ s="B /home/BL/004_010_0100.0 23 0.031"
$ num=$( awk '{ print $(NF-1) }' <<<"$s" )
$ printf 'num is "%s"\n' "$num"
num is "23"
如果您的数据来自$s
命令,那么您可以awk
直接将其输入,而不是将其存储在中间变量中:
num=$( some-command | awk '{ print $(NF-1) }' )
grep
是一个返回匹配的工具线-o
(忽略该工具的某些实现中可用的非标准选项)。如果我们首先根据字符串中的空格将字符串转换为多行,我们可以用来grep
挑选数字:$s
$ tr -s '[:blank:]' '[\n*]' <<<"$s" | grep -x '[[:digit:]]\{1,\}'
23
这里使用的命令tr
将字符串从
B /home/BL/004_010_0100.0 23 0.031
进入
B
/home/BL/004_010_0100.0
23
0.031
和grep
命令选出了这一行仅有的由数字组成(该-x
选项将强制给定的模式匹配完整的行)。显然,只有当您要查找的数字是正整数时,这才有效。
如果您知道您会对倒数第二个“字段”感兴趣,那么您可以使用tail
andhead
代替:
$ tr -s '[:blank:]' '[\n*]' <<<"$s" | tail -n 2 | head -n 1
23
... 或者sed
:
$ tr -s '[:blank:]' '[\n*]' <<<"$s" | sed -n -e '${ g; p; }' -e h
23
上述所有变体都是标准且便携式的。cut
如果我们使用非标准rev
实用程序反转该行两次,我们还可以用来提取倒数第二个字段:
$ rev <<<"$s" | tr -s '[:blank:]' '[\t*]' | cut -f 2 | rev
23
在这里,我们还聘请tr
制表符替换所有空白字符(并将它们压缩到单身的选项卡)。 cut
然后简单地提取第二个字段,然后rev
再次反转提取的数据。
答案2
你可以用 Perl 尝试一下:
echo "$s" | perl -e 'for(<>){/B\s+.*?\s+(\d+)\s+/;print $1}'
在这里我们找到带有以下内容的字符串:
B
特点- 后跟一个或多个空格字符 -
\s+
- 后跟第一个空格字符之前的所有惰性字符 -
.*?\s+
- 接下来是我们想要的数字 - 将其捕获在括号中的捕获组中
(\d+)
- 它保存在$1
特殊变量中 - 后跟一个或多个空格字符 -
\s+
。
这个正则表达式可以被改进(例如使用^
和$
运算符来指出字符串的开头和结尾)。
阅读更多关于正则表达式。
答案3
如果您有权访问 GNU grep
(Linux 系统上的默认设置),则此正则表达式将捕获不带任何小数的数字。
grep -oP '\b(?<!\.)\d+(?!\.)\b'
正则表达式解释:
\b
匹配单词边界(?<!\.)
负向后查找断言后面没有小数点(.
)\d+
匹配一个数字一次或多次(?!\.)
负向先行断言前面没有小数点(.
)\b
匹配单词边界
答案4
只是改变了grep
一点。如果您使用 GNU grep
(Linux 系统上的默认设置),这应该可以工作:
s="B /home/BL/004_010_0100.0 23 0.031"
num=$(echo $s | grep -oP '\.*?\s+\d+\s+')