在 shell 脚本中提取子字符串“mode:”后的数值的简洁但可读的方法

在 shell 脚本中提取子字符串“mode:”后的数值的简洁但可读的方法

我有这个字符串:

DMT           mode 4: 640x480 @ 60Hz 4:3, clock:25MHz progressive

我想从中提取模式编号(第一个 :) 之前的部分,在本例中为 4。正如您所期望的,模式编号可能是 1 或 2 位数字长,并且不能依赖它之前的文本字符长度完全相同。

我有一个有效的解决方案:

$picked = "DMT           mode 4: 640x480 @ 60Hz 4:3, clock:25MHz progressive"
echo $picked | awk -F"mode " '{print $2}' | tr : '\n' | head -n1

但我觉得必须有一种更优雅的方法来做到这一点。优雅且易于学习,稍后再阅读(因此,可能不涉及正则表达式)。我梦想的命令是这样的:回声 $picked |在“模式”“:”之间

这里还有几个例子来展示需要可解析的输入范围:

CEA           mode 7: 720x480 @ 60Hz 16:9, clock:27MHz x2 interlaced
CEA  (native)  mode 16: 1920x1080 @ 60Hz 16:9, clock:148MHz progressive
DMT           mode 58: 1680x1050 @ 60Hz 16:10, clock:146MHz progressive

答案1

正则表达式将是更直接的解决方案:一些选项:

echo "$picked" | grep -oP '(?<=mode )\d+'
echo "$picked" | grep -oP '(?<=mode )[[:digit:]]+'

如果您不喜欢 PCRE 功能:

echo "$picked" | grep -oE 'mode [[:digit:]]+' | tr -d 'mode '

tr命令不会删除单词“模式”,它删除所有人物“模式”, ” ”。


如果你真的喜欢,迭代单词直到点击“模式”

echo "$picked" | awk '{for (i=1; i<NF; i++) if ($i == "mode") {print $(i+1); exit}}' | tr -d :

由于 shell 变量中已包含该字符串,因此 bash 参数替换如何:

tmp=${picked#*mode }    # remove up to "mode "
value=${tmp%%:*}        # remove the colon and everything after

然后

$ declare -p picked tmp value
declare -- picked="DMT           mode 4: 640x480 @ 60Hz 4:3, clock:25MHz progressive"
declare -- tmp="4: 640x480 @ 60Hz 4:3, clock:25MHz progressive"
declare -- value="4"

参考3.5.3 Shell参数扩展在手册中(并掩盖了一些细节):

  • ${var#pattern}去除最短前缀匹配模式
  • ${var##pattern}去除最长前缀匹配模式
  • ${var%pattern}去除最短后缀匹配模式
  • ${var%%pattern}去除最长后缀匹配模式

“最短”和“最长”之间的区别是关键。请注意,给定的字符串后缀中包含多个冒号——使用${tmp%:*}仅删除最后的冒号及其后的字符。

答案2

如果您有支持 PCRE 样式正则表达式perl的或其变体,您可以通过直接匹配后跟一个或多个数字来grep选择值:mode

grep -oP 'mode\s+\K\d+'

我读到您不喜欢 RE,但是这是一个相当简单的模式,我将在这里解释:

  • 一切 upto\K都是必须匹配的后向模式,但不包含在任何结果中
  • \s匹配空白(通常空间或者标签);后缀+需要其中一个或多个
  • \d匹配数字 ( 0.. 9);后缀+需要其中一个或多个

如果您知道所需的值可能包含非数字文本,则可以替换\d+为。[^:]+此处,[^:]匹配除冒号 ( :) 之外的任何内容。

参考

答案3

简单直接的sed解决方案是

sed -n 's/.*mode \([0-9]*\):.*/\1/p'

单个命令而不是三个 ( awk | tr | head);如果您认为您的解决方案更容易阅读,我无法帮助您。

答案4

我喜欢已经给出的 Shell 参数扩展解决方案,但如果您不能使用 bash,以下 AWK 链将以相同的方式工作:

... |awk -F"mode " '{print$2}' | awk -F: '{print $1}'

第一个 awk 调用在“mode”上分割字符串并返回其后面的内容,

第二次调用将其拆分为 : 并返回其之前的内容。

对我来说,这比 Bash 参数扩展更具可读性。然而,它更加冗长,并且可能更慢(尽管众所周知 AWK 相当快,但启动它两次的开销将对其产生不利影响)。

相关内容