sed正则表达式:组重复选项?

时间:2019-02-07 15:39:07

标签: regex bash sed

我有几行文字输入。每个组用空行(\ n \ n)分隔。 我正在使用sed进行处理,但是我愿意接受其他选择。

我正在使用这种结构来一次处理所有行:

# if the first line copy the pattern to the hold buffer
1h
# if not the first line then append the pattern to the hold buffer
1!H
# if the last line then ...
$ {
  # copy from the hold to the pattern buffer
  g

  ... here are my regex lines.

  # print
  p
}

每组的目标输出是每行,但第一行以第一行的内容为前缀,并以空格分隔。

由于我当前的输入仅包含2、3和6行,因此我对其进行了“硬编码” 像这样:

2行: s/\n\n\([^\n]\+\)\n\([^\n]\+\)\n\n/\n\n\1 \2\n\n/g

3行: s/\n\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\n/\n\n\1 \2\n\n\1 \3\n\n/g

6行: s/\n\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\([^\n]\+\)\n\n/\n\n\1 \2\n\n\1 \3\n\n\1 \4\n\n\1 \5\n\n\1 \6\n\n/g

(由于每组正则表达式行我都有两次,因为一个组的结尾\ n \ n可能是必需的,但不适用于匹配下一个组的开头)

我正在寻找一种通用方法,该方法适用于2到n行的任意大小的组。有人对此有任何想法吗?

更新:因为@Benjamin W.请求了示例输入/输出:

我要在这里解决的真正问题是为温度记录守护程序动态生成csv标头行,该守护程序源于sensors -u中的数据。 (因为在关闭笔记本计算机电源后,输出顺序似乎会发生变化)

使用sed可以很容易地从原始程序输出到此:

jc42-i2c-0-1a SMBus I801 adapter at f040
temp1

asus-isa-0000 ISA adapter
cpu_fan
temp1

acpitz-acpi-0 ACPI interface
temp1

jc42-i2c-0-18 SMBus I801 adapter at f040
temp1

coretemp-isa-0000 ISA adapter
Package id 0
Core 0
Core 1
Core 2
Core 3

我上面提到的3条sed regex替换行允许我将其转换为:

jc42-i2c-0-1a SMBus I801 adapter at f040 temp1
asus-isa-0000 ISA adapter cpu_fan
asus-isa-0000 ISA adapter temp1
acpitz-acpi-0 ACPI interface temp1
jc42-i2c-0-18 SMBus I801 adapter at f040 temp1
coretemp-isa-0000 ISA adapter Package id 0
coretemp-isa-0000 ISA adapter Core 0
coretemp-isa-0000 ISA adapter Core 1
coretemp-isa-0000 ISA adapter Core 2
coretemp-isa-0000 ISA adapter Core 3

但这当然仅适用于适配器的机器,每个适配器具有1、2或5个值。

更新2019-02-11:

因此,在得到两个建议通用解决方案的答案之后,我再次研究了这个问题,并简化了我的整个温度记录脚本:

echo -n "timestamp"
sensors -u | # -u gives Raw output, suitable for easier post-processing
grep --invert-match '  ' | # remove all lines containing values, leaving only headers
sed -n 'H; ${x; s/\nAdapter: / /g; p}' | # join headers spanning two lines together. For syntax see: https://unix.stackexchange.com/questions/163428/replace-a-string-containing-newline-characters & http://www.grymoire.com/Unix/Sed.html#uh-55
sed 'N;/\n$/d;s/\(.*\)\n\(.*\):/\1 \2\n\1/;P;$d;D' | # join the headers header with each sub-header, see: https://stackoverflow.com/questions/54576948/sed-regex-group-repeat-option
tr '\n' ';' | sed 's/.$//' # join finished headers together in a single line sepearted by ; & remove the trailing ;
echo ""

while true
do
    ts=`date +"%Y-%m-%d %H:%M:%S"`
    echo -n "$ts;"
    sensors -u | grep --invert-match '_max\|_crit\|_min' | # remove min max crit values which represent config, not state.
    grep '\.' | # remove all non value lines left (headers & empty lines seperating blocks
    sed 's/  .*: //g' | # remove value names, leaving only the values themselfs
    sed 's/\.000//g' | # remove empty decimals
    tr '\n' ';' | sed 's/.$//' # join finished values together in a single line sepearted by ; & remove the trailing ;
    sleep 1
    echo ""
done

2 个答案:

答案 0 :(得分:2)

这可能对您有用(GNU sed):

sed 'N;/\n$/d;s/\(.*\)\n\(.*\)/\1 \2\n\1/;P;$d;D' file

将下一行追加到当前行。

如果追加的行为空,即\n$表示为空行,请完全删除模式空间,然后像没有消耗任何行一样继续进行操作。

否则,模式空间中的两行都是非空的,因此请将这两行转换为一条,然后将第一行追加到结果中。

打印图案空间中的第一行。

如果它是文件的最后一行,则删除模式空间。

删除图案空间中的第一行。

重复。

D删除模式空间中的第一行,并且如果模式空间不为空,则不会用下一行隐式替换模式空间。

答案 1 :(得分:2)

这可以作为awk解决方案:

awk 'BEGIN {RS="\n\n"; FS="\n"} {for (i = 2; i <= NF; i++) print $1,$i}' file
  • 将“ \ n \ n”定义为记录分隔符(RS)
  • 将“ \ n”定义为字段分隔符(FS)
  • 对于从第二个到最后一个(NF)的每个记录中的每个字段:打印由OFS组合的第一个字段($ 1)和当前字段($ i),由“,”触发