sed 查找第 N 次出现

sed 查找第 N 次出现

我在基于 Linux 的 Synology 平台上有以下字符串,我想提取一些值:

{"report":"Instantaneous values:<BR>voltage=243.5 Vrms<BR>FFTComponents:<BR>Phase 1:<BR>\tcurrent=0.348 A, activePower=68.461 W, reactivePower=50.175 var, apparentPower=84.879 VA, cosfi=80, quadrant=0, phaseshift=0.0, phaseDiff=0.0<BR>\tFFTComponents:<BR>Phase 2:<BR>\tcurrent=0.076 A, activePower=2.888 W, reactivePower=18.492 var, apparentPower=18.717 VA, cosfi=10, quadrant=0, phaseshift=0.0, phaseDiff=0.0<BR>\tFFTComponents:<BR>Phase 3:<BR>\tcurrent=1.431 A, activePower=299.807 W, reactivePower=177.96 var, apparentPower=348.646 VA, cosfi=85, quadrant=0, phaseshift=0.0, phaseDiff=0.0<BR>\tFFTComponents:<BR><BR><BR>Phase 1, peak active power 5570.098 W at 03/09/2022 14:18:10<BR>Phase 2, peak active power
4562.172 W at 25/09/2022 09:21:45<BR>Phase 3, peak active power 3188.103 W at 07/11/2022 16:35:35<BR>active energy RMS per phase mapping combination<BR>phase mapping 210=372.779 kWh [ 1/1]<BR>phase mapping 12=808.956 kWh [* 1/3]<BR>phase mapping 21=307.154 kWh [
-1/1]<BR>phase mapping 102=321.293 kWh [ -1/2]<BR>phase mapping 120=508.832 kWh [ 1/0]<BR>phase mapping 201=317.701 kWh [
-1/1]<BR><BR>active energy RMS (solar) per phase mapping combination<BR>phase mapping 210=0.0 kWh [ 1/1]<BR>phase mapping 12=0.0 kWh [* 1/3]<BR>phase mapping 21=0.0 kWh [ -1/1]<BR>phase mapping 102=0.0 kWh [ -1/2]<BR>phase mapping 120=0.0 kWh [ 1/0]<BR>phase mapping 201=0.0 kWh [ -1/1]<BR><BR>"}

我在互联网上找到了一些代码。该代码有效,但是,它没有提供我想要的所有信息。

https://github.com/apazga/smappee-domoticz-bash/blob/master/smappee_bash_extractor.sh

代码部分用于sed查找字符串urrent=,并返回该字符串后面的值。

AMPS=`echo $SMAP |sed -e 's|.*urrent=\(.*\)|\1|' -e 's|\(.\{1,4\}\).*|\1|'`

我想将其分为 AMPSL1、AMPSL2 和 AMPSL3

  • AMPL1:必须搜索 current 的第一次出现并返回 0.348
  • AMPL2:必须搜索 current 的第二次出现并返回 0.076
  • AMPL3:必须搜索 current 的第三次出现并返回 1.431

我已经发现以下代码返回最后一次出现的情况

  AMPSL3=`echo $SMAP |sed -e '$s|.*urrent=\(.*\)|\1|' -e 's|\(.\{1,4\}\).*|\1|'`

有人可以帮我吗?

答案1

正则表达式的问题是它们无法计数。因此,对于要提取的每个值,您需要一个不同的、复杂的正则表达式。相反,我会使用grepbeforesed来隔离所需的值:

$ AMPS=$(echo "$SMAP" | grep -oE 'current=[0-9]+\.[0-9]+' | sed -E 's|current=||')
$ echo "$AMPS"
0.348
0.076
1.431

head然后可以使用和的组合tail来提取各个值。

$ AMPSL1=$(echo "$AMPS" | head -1)
$ echo $AMPSL1
0.348
$ AMPSL2=$(echo "$AMPS" | tail +2 | head -1)
$ echo $AMPSL2
0.076
$ AMPSL3=$(echo "$AMPS" | tail +3 | head -1)
$ echo $AMPSL3
1.431

或者正如 terdon 所建议的,“如果使用的话,你可以避免双头/尾awk”。

$ AMPSL2=$(echo "$AMPS" | awk 'NR==2')
$ echo $AMPSL2
0.076

答案2

如果您有 GNU grep(在嵌入式系统上可能没有),您可以这样做:

read ampl1 ampl2 ampl3 < <(grep -oP 'current=\K[0-9.]+' <<<"$amps" | tr '\n' ' ' | sed 's/$/\n/' )

该习惯用法允许您使用进程替换将read var < <(command)的输出保存command到当前 shell 中的变量中。var

这是用于grep -o仅打印输入的匹配部分,-P对于 PCRE 正则表达式,它为我们提供了\K“忘记此处之前匹配的所有内容”的符号。然后,我们需要将空格转换为换行符,tr并添加尾随换行符,以sed准备好将所有内容传递给read存储到变量中的内置函数。输出是:

$ read ampl1 ampl2 ampl3 < <(grep -oP 'current=\K[0-9.]+' <<<"$amps" | tr '\n' ' ' | sed 's/$/\n/' )
$ echo "AMPL1: $ampl1 AMPL2: $ampl2 AMPL3: $ampl3"
AMPL1: 0.348 AMPL2: 0.076 AMPL3: 1.431

如果你不能使用grep -oP,你可以这样做:

$ read ampl1 ampl2 ampl3 < <(perl -007 -ne '@m=(/current=([0-9.]+)/g); print "@m\n"' a)
$ echo "AMPL1: $ampl1 AMPL2: $ampl2 AMPL3: $ampl3"
AMPL1: 0.348 AMPL2: 0.076 AMPL3: 1.431

这个答案需要你使用bashshell。由于 Synology 系统上的默认 shell 是以bashPOSIX 模式运行的 shell,因此<(...)不启用进程替换 ( )。您必须首先bash简单地输入bash

或者,如果您将在脚本中使用这些命令,请使用bash scriptName.sh.

相关内容