Bash 脚本,用于将行的内容替换为后续行中的信息 (sed)

Bash script to replace content of line with information in a consequent line (sed)

提问人:Enialis 提问时间:9/17/2023 更新时间:9/18/2023 访问量:86

问:

我有一个 File.txt,在方括号 [] 中包含的后续行中列出了几个项目和其他详细信息:

item name1,
item name2,
item name3,
item name4,
some text
on several lines
detail name1;
detail name2[moreinfo];
detail name3[can contain numbers and characters:1];
detail name4;

我想修改它,以便在前面的行中附加每个项目的详细信息,以获得以下预期输出:

item name1,
item name2[moreinfo],
item name3[can contain numbers and characters:1],
item name4,
some text
on several lines
detail name1;
detail name2[moreinfo];
detail name3[can contain numbers and characters:1];
detail name4;

当项目数量未知时,是否有任何简单的 sed 表达式来执行此操作? 我想要这样的东西:

sed -i '/detail \(.*\)\[\(.*\)\]/ s/item \1/item \1\[\2\]/g' File.txt

但我知道反向引用不是这样工作的。

正则表达式 bash perl sed

评论

1赞 zdim 9/17/2023
嗯......标题写着“Bash script”,在你特别要求“easy sed expression”的问题中,而标签包含“perl”。它是什么?他们中的任何一个?如果是这样,请删除对特定语言的明确提及(并显示您尝试过的内容sed)
0赞 Fravadona 9/17/2023
这似乎很难做到sed
0赞 markp-fuso 9/17/2023
一个字段在方括号内可以有不同的内容吗?例如,是否可能发生以下情况:+,如果可以发生这种情况,那么预期的输出是什么?(此外,如果可能发生这种情况,请更新示例数据和预期输出以演示问题)nameXitem name5[some_info]details name5[more_info]

答:

4赞 choroba 9/17/2023 #1

下面是一个Perl解决方案:

perl -0777 -pe 's/^item (.*),(?=(?:.|\n)*^detail \1(\[.*\]))/item $1$2,/mg'

它使用 (可以缩短为 Perl 5.36 之后)将整个文件作为单个字符串读入。替换使用前瞻断言来搜索相应的详细信息。-0777-g(?=...)

评论

2赞 Gilles Quénot 9/17/2023
perl -g使用 Perl >= 5.36 而不是-0777
0赞 choroba 9/17/2023
@GillesQuénot:感谢您的评论,我已经更新了答案。
1赞 Ed Morton 9/17/2023 #2

使用和任何:tacawk

$ tac file |
    awk -F'[[ ,;]' '
        ($1 == "detail") && match($0,/\[.*]/) { det[$2] = substr($0,RSTART,RLENGTH) }
        ($1 == "item") && ($2 in det) { sub(/,$/,""); $0 = $0 det[$2] "," }
        { print }
    ' |
    tac
item name1,
item name2[moreinfo],
item name3[can contain numbers and characters:1],
item name4,
some text
on several lines
detail name1;
detail name2[moreinfo];
detail name3[can contain numbers and characters:1];
detail name4;
0赞 potong 9/17/2023 #3

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

sed -E 'H;$!d;x
        :a;s/(\nitem ([^,]+))(,.*\ndetail \2(\[[^[]+\]);)/\1\4\3/;ta;s/.//' file

将文件放入保留空间,然后对项目和详细信息使用相同名称的模式匹配来迭代替换,在匹配的项目名称后在详细信息后面插入一个括号字符串。当没有其他匹配项时,删除引入的前导换行符并打印结果。

稍短的替代解决方案:

sed -zE ':a;s/(item ([^,]+))(,.*detail \2(\[[^[]+\]);)/\1\4\3/;ta' file
1赞 ufopilot 9/17/2023 #4
awk '
    FNR==NR{
        if ($0 ~ /^detail /){
            sub(/^detail /,"item ")
            sub(/;$/,",")
            key=value=$0
            sub(/\[.*\]/,"",key)
            a[key] = value
        }
        next
    }   
    {
        if ($0 in a) 
            print a[$0]
        else
            print
    }
' file file