como acrescentar linha somente se a linha não for definida após uma palavra específica

como acrescentar linha somente se a linha não for definida após uma palavra específica

Eu construo o seguinte sed cli para adicionar a seguinte linha

force_https_protocol=PROTOCOL_TLSv1_2

depois da linha:

[security]

o sed cli:

sed  -i '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2'  /etc/ambari-agent/conf/ambari-agent.ini

mas o problema é quando a fila force_https_protocol=PROTOCOL_TLSv1_2 já está depois[security]

como alterar a sintaxe sed para ignorar a adição da linha se a linha já existir?

resultado esperado

[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

arquivo errado:

[security]
force_https_protocol=PROTOCOL_TLSv1_2
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

Responder1

Supondo que todos os "force_https_protocol=PROTOCOL_TLSv1_2" no arquivo estejam dentro da tag [security] e não apareçam em nenhum outro lugar

sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
    -e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file

A ideia é deletar todas as linhas do arquivo que contém (apenas) a string "force_https_protocol=PROTOCOL_TLSv1_2".

Explicação

/^force_https_protocol=PROTOCOL_TLSv1_2$/d'

Exclua todas as linhas que contêm a string "force_https_protocol=PROTOCOL_TLSv1_2".

Testes

$ cat file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

$ sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
      -e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

Responder2

1. Usando AWK

Aqui está uma possível solução para o problema mais geral de adicionar umcordapara um bloco de texto rotulado (ou seja, as linhas de texto que seguem um rótulo específico em um arquivo e precedem o próximo rótulo ou o local do arquivo) somente secordaainda não está presente.

Uma abordagem simples é percorrer o arquivo de trás para frente e acompanhar a última vez que encontramoscorda, imprima-o sempre que encontrarmos uma etiqueta específica (pouco antes de imprimir a etiqueta em si), mas apenas se não a tivermos visto (corda) ainda, e esqueça de tê-lo encontrado (corda) sempre que nos deparamos com um rótulo. E, claro, inverta o resultado como última etapa.

tac file | awk '
    /^force_https_protocol=PROTOCOL_TLSv1_2$/ {
        seen = 1
    }
    /^\[security\]$/ {
        if ( ! seen ) {
            print "force_https_protocol=PROTOCOL_TLSv1_2"
        }
    }
    /^\[[^]]*\]$/ {
        seen = 0
    }
    1' | tac

tac, incluído emGNU Coreutils, concatena e reverte arquivos linha por linha. Como reverter um arquivo com outras ferramentas/padrão é abordado em várias perguntas e respostas sobre U&L (por exemplo,Como o comando sed '1!G;h;$!d' reverte o conteúdo de um arquivo?eGrepping reverso).

Isso não será inserido force_https_protocol=PROTOCOL_TLSv1_2após o primeiro [security]trecho de texto a seguir:

[security]
keysdir=/var/lib/ambari-agent/keys
force_https_protocol=PROTOCOL_TLSv1_2
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
...

Para simplesmente inserircordaapós cada ocorrência de um rótulo, a menos quecordajá existe, a mesma abordagem pode levar a:

tac file | awk '
    /^force_https_protocol=PROTOCOL_TLSv1_2$/ {
        seen = NR
    }
    /^\[security\]$/ {
        if ( ( NR == 1 ) || ! ( NR == seen + 1 ) ) {
            print "force_https_protocol=PROTOCOL_TLSv1_2"
        }
    }
    1' | tac

(Essevaiinsira force_https_protocol=PROTOCOL_TLSv1_2depois [security]no exemplo de texto acima).

2. Usando sed

Com sedvocê pode usar uma variação do N;P;D;ciclo (que é abundantemente abordada em outras perguntas e respostas sobre U&L; por exemplo:Como posso usar o sed para substituir uma string multilinha?ouComo editar a próxima linha após o padrão usando sed?).

Isto irá inserir a linha force_https_protocol=PROTOCOL_TLSv1_2após uma linha composta apenas [security]se a primeira ainda não estiver lá.

sed '
  $! {
    N
    P
    /^\[security\]\n/ {
      /\nforce_https_protocol=PROTOCOL_TLSv1_2$/ b b
      i\
force_https_protocol=PROTOCOL_TLSv1_2
    }
    :b
    D
  }
  s/^\[security\]$/&\
force_https_protocol=PROTOCOL_TLSv1_2/'

Este roteiro:

  • para cada linha, exceto a última ( $!), anexa uma nova linha seguida pela próxima linha ao espaço padrão ( N) (que já contém a linha atual);
  • imprime a primeira linha no espaço padrão ( P); se essa linha for [security]...
    • ...e a ​​próxima linha é force_https_protocol=PROTOCOL_TLSv1_2, apenas exclui a primeira linha do espaço padrão e inicia um novo ciclo ( b b, :b D);
    • caso contrário, imprime force_https_protocol=PROTOCOL_TLSv1_2na saída padrão ( i\) antes de excluir a primeira linha do espaço padrão e iniciar um novo ciclo ( D);
  • se a última linha for [security], substitui-a por ela mesma seguida por uma nova linha e force_https_protocol=PROTOCOL_TLSv1_2imprime o resultado (por meio do implícito p).

informação relacionada