Substituição do texto correspondente após duas linhas correspondentes

2

Eu tenho um arquivo YAML que inclui uma estrofe:

admin::common::passwords:
  alice:
    password: '$6$oTQhLvN/4VFJPscD$8LYwUMSFi'
  bob:
    password: '$6$JKOtLF0wHeZfIskt$W/M5.ugDS'

Se a variável de shell $ ACCOUNT contiver o nome da conta (alice, bob, etc) e $ YAML for o nome do arquivo YAML, posso adicionar uma nova entrada com sed, como esta. Ele insere a nova conta diretamente sob a linha "admin :: common :: passwords:", então eu sei que ela está na sub-rotina correta:

  sed -i /'^admin::common::passwords:'/a"\  ${ACCOUNT}:\n    password: '${ENCRYPTED_PASS}'" $YAML

Eu posso encontrar uma conta existente como esta com o awk. Novamente, isso encontra a estrofe, depois encontra a conta dentro daquela estrofe (provavelmente é possível fazer isso tudo no awk, mas eu não sou bom o suficiente no awk, e isso fará o trabalho):

  awk "BEGIN{RS=ORS="\n\n";FS=OFS="\n"}/admin::common::passwords:/" $YAML | grep -A1 "^[ ]*${ACCOUNT}:"

Mas, para excluir uma linha, o melhor que posso fazer é isto:

sed -i "/^  ${ACCOUNT}:/,+1d" $YAML

O que excluiria qualquer linha "^ alice:" em qualquer parte do arquivo, seja na sub-rotina admin :: common :: passwords ou em outro lugar.

Eu não posso fazer nenhuma suposição sobre o conteúdo do arquivo, exceto que ele contém exatamente uma sub-rotina admin: common: passwords (caso contrário, ele teria sido criado por outra parte do meu script; esse bit é fácil).

Então, minha pergunta é: Como posso melhorar esse sed para dizer ' Procure por "^ $ {ACCOUNT}:" dentro da sub-rotina admin :: common :: passwords , e excluir essa linha correspondente, assim como a linha abaixo dela '?

(não precisa ser sed, é claro; pode ser que awk ou mesmo Perl sejam mais adequados para a tarefa)

    
por Steve Parker 10.11.2015 / 14:16

2 respostas

2
sed "
  /^admin::common::passwords:$/,/^[^ ].*:$/ {
     /^  $ACCOUNT:$/ {
       N;d
     }
  }" < "$YAML"

Ou seja, inclua sua sub-rotina de exclusão em uma seção que corresponda entre admin::common... e a próxima whatever-section: .

Tenha em atenção que o carácter . , comum nos nomes de utilizador, também é um operador de expressão regular, pelo que john.doe corresponderia a john.doe , mas também johnWdoe , por exemplo.

Observe que o acima não funcionará se você tiver duas seções admin::common::passwords consecutivas e a conta a ser excluída estiver na segunda.

Se, como Otheus indica , as seções são executadas até que a próxima linha tenha a mesma ou menor quantidade de caracteres de espaço à esquerda e sua seção admin::common::passwords: possa ter alguns espaços à esquerda, então provavelmente é hora de mudar para outro idioma como awk :

awk -v account="$ACCOUNT" '
  match($0, /[^ ]/) && RSTART <= n {n = 0}
  n && NF == 1 && $1 == account ":" {getline; next}
  !n && /^ *admin::common::passwords:$/ {
    match($0, /[^ ]/)
    n = RSTART
  }
  {print}' < "$YAML"
    
por 10.11.2015 / 14:33
1

Seria muito mais seguro usar bibliotecas pré-construídas para analisar o arquivo inteiro (assim, por exemplo, link ) faça suas modificações e salve novamente o arquivo modificado. Fazer modificações de texto um pouco cegas em um arquivo de texto estruturado como este é apenas pedir algo para dar errado.

    
por 10.11.2015 / 14:23