Exclua linhas entre 2 padrões, mas somente se houver uma string específica entre eles

Exclua linhas entre 2 padrões, mas somente se houver uma string específica entre eles

Exemplo, tenho um arquivo com o seguinte conteúdo:

eggs
bacon
cereal

eggs
bacon
cheese
cereal

Saída:

eggs
bacon
cereal

No exemplo, quero excluir as linhas entre as palavras eggse cereal, mas apenas se contiver cheeseentre elas.

Como eu poderia fazer isso usando sed?

Pediram-me para ser mais específico, então aqui está basicamente o que quero fazer e espero que isso deixe tudo mais claro:

WeaponData
{
    TextureData
    {
                "crosshair"
                {
                                "file"  "vgui/replay/thumbnails/"
                                "x"             "0"
                                "y"             "0"
                                "width"         "64"
                                "height"        "64"
                }
                "weapon"
                {
                                "file"          "sprites/bucket_bat_red"
                                "x"             "0"
                                "y"             "0"
                                "width"         "200"
                                "height"                "128"
                }
                "weapon_s"
                {       
                                "file"          "sprites/bucket_bat_blue"
                                "x"             "0"
                                "y"             "0"
                                "width"         "200"
                                "height"                "128"
                }

                "ammo"
                {
                                "file"          "sprites/a_icons1"
                                "x"             "55"
                                "height"    "15"
                }

                "crosshair"
                {
                    "file"      "sprites/crosshairs" <---
                    "x"     "32"
                    "y"     "32"
                    "width"     "32"
                    "height"    "32"
                }

                "autoaim"
                {
                    "file"      "sprites/crosshairs"
                    "x"     "0"
                    "y"     "48"
                    "width"     "24"
                    "height"    "24"
                }
    }
}

Este é o comando que tentei:

sed '/"crosshair"/,/}/d' file.txt

Esse comando apenas exclui do primeiro "crosshair"ao último }, independentemente de ter "sprites/crosshairs"ou não a linha intermediária.

Eu só quero deletar de "crosshair"para }se entre os padrões a string "sprites/crosshairs"for encontrada. No entanto, existem outros blocos de código no arquivo de texto contendo "sprites/crosshairs".

Como:

"autoaim"
{
        "file"      "sprites/crosshairs"
        "x"     "0"
        "y"     "48"
        "width"     "24"
        "height"    "24"
}

Isso não pode ser removido. Peço desculpas PROFUNDAS por não explicar isso antes, mas não achei que seria necessário.

A sugestão de don_crissti funciona muito bem, o que ésed '/crosshair/,/\}/{H;/\}/!d;s/.*//;x;/sprites\/crosshairs/d;s/.//;}' infile

A saída é esta, no entanto:

              ...
              "autoaim"
              {
  }
}

"autoaim"foi parcialmente excluído, mas não completamente, mas como você pode ver o bloco que contém "sprites/crosshairs"foi removido, como eu queria fazer, mas autoaimainda não pode ser modificado

Responder1

sed '/^[[:blank:]]*"crosshair"/,/}/{H;/}/!d;s/.*//;x;/sprites\/crosshairs/d;s/.//;}' infile

Como funciona:

sed '/^[[:blank:]]*"crosshair"/,/}/{         # in this range
H                                            # append each line to hold buffer
/}/!d                                        # delete it if not the end of range
s/.*//                                       # empty the pattern space
x                                            # exchanges buffers
/sprites\/crosshairs/d                       # delete pattern space if it matches
s/.//                                        # remove leading newline, autoprint
}' infile

Isso pressupõe que as linhas correspondentes ^[[:blank:]]*"crosshair"são sempre seguidas por um bloco de linhas entre colchetes, assim como no seu exemplo.

Responder2

se perlestiver tudo bem, pode-se usar o modo de parágrafo ( -00opção) e usar a correspondência de regex multilinha

$ cat ip.txt 
eggs
bacon
cereal

eggs
bacon
cheese
cereal

$ perl -00 -ne 'print if !/^eggs.*cheese.*cereal$/ms' ip.txt 
eggs
bacon
cereal

informação relacionada