Como fazer e aplicar (patch) diff de um lado?

4

Eu quero fazer um script de shell que atualize um arquivo de configuração.

O arquivo presente no servidor possui informações, como vários endereços IP.

O novo arquivo tem mais código, a partir de novas configurações adicionadas ao sistema, eu quero poder adicionar essas novas configurações ao arquivo do servidor sem alterar o que já está configurado lá

exemplo:

arquivo do servidor

[config]
ip=127.0.0.1
port=22

novo arquivo

[config]
ip=XXX.XXX.XXX.XXX
port=XX
user=root

Eu quero que o arquivo resultante seja

[config]
ip=127.0.0.1
port=22
user=root

Como seria uma boa maneira de fazer isso? Eu não quero confiar na posição da linha e tal, porque os arquivos de configuração reais são muito grandes e mais linhas poderiam ter sido adicionadas ao arquivo do servidor.

Eu tentei fazer um diff dos arquivos e aplicar o patch, mas não funcionou.

Obrigado por qualquer ajuda.

    
por DaneoShiga 23.08.2012 / 21:38

3 respostas

6

Como diz Gilles, você precisará usar uma ferramenta que conheça o formato de seus arquivos de configuração. Para o seu exemplo específico, você pode usar um script Python curto que usa o módulo interno ConfigParser .

  • Primeiro, digamos que seu arquivo de configuração original no servidor seja original.cfg :

    [config]
    ip=127.0.0.1
    port=22
    
  • Agora, coloque suas alterações em um novo arquivo chamado update.cfg :

    [config]
    user=root
    

    Este arquivo deve ter entradas novas ou alteradas listadas sob o cabeçalho da seção onde você gostaria que elas fossem enviadas.

  • Em seguida, execute um script como este:

    #!/usr/bin/env python
    
    import ConfigParser
    
    config = ConfigParser.ConfigParser()
    
    # Read the original config file
    with open('original.cfg', 'r') as f:
        config.readfp(f)
    
    # Also read in all the changes we'd like to make
    with open('update.cfg', 'r') as f:
        config.readfp(f)
    
    # Write the full new config file out
    with open('output.cfg', 'w') as f:
        config.write(f)
    

Existem, claro, muitas variações sobre como fazer isso. Por exemplo, as alterações de configuração podem ser codificadas diretamente no script Python, em vez de serem lidas a partir de um arquivo update.cfg separado. Isso deve lhe dar uma boa base para começar.

    
por 23.08.2012 / 23:26
4

Analise-os em dois conjuntos de dados separados com qualquer programa que você preferir. Eu usaria o common lisp e armazenaria cada arquivo de configuração como uma lista aninhada de listas.

E, em seguida, use as ferramentas de idioma para fazer a mesclagem / correção / união / diferença de conjunto / etc. Em um nível alto, para um diff de um lado, o código pode parecer com:

(union
  (vals A :keys 'all)
  (vals B
    :keys  
    (set-difference
      (keys B)
      (keys A))))

E, em seguida, escreva o conjunto de dados resultante em um arquivo.

    
por 24.08.2012 / 02:45
3

Eu uso a biblioteca Python ConfigObj para isso. Se você fizer isso, será necessário escrever Python para fazer interface com os arquivos de configuração. Eu normalmente tenho um arquivo default_conf que tem valores padrão, e eu posso seletivamente substituir essas configurações com algum outro arquivo, digamos user_conf

Aqui está o código que estou usando atualmente como exemplo. A segunda metade da função é validação, o que é importante, mas você pode ignorá-la para uma primeira aproximação. Para uso você faz

conf = get_conf()
ip = conf["ip"]

No seu caso, acho que o arquivo do servidor é aquele que se sobrepõe, embora mais comumente seja o arquivo do usuário que substitui o arquivo do sistema. Essa é certamente a convenção do usuário em sistemas do tipo Unix.

Para escrever um objeto ConfigObj para arquivo, para uma primeira aproximação você faz (se config for seu objeto como no script abaixo)

config.filename = filename
config.write()

Assim, você pode modificar a função abaixo para gravar em um arquivo, em vez de retornar o objeto de configuração, se desejar. Veja Escrevendo um arquivo de configuração para um pouco mais de detalhes.

def get_conf():
    import configobj, validate, sys
    try:
        config = configobj.ConfigObj('default_conf', configspec='conf.validate', raise_errors=True)
    except configobj.ConfigObjError:
        print "ERROR FOR CONFIG FILE 'default_conf':"
        raise
    try:
        user = configobj.ConfigObj('user_conf', configspec='conf.validate', raise_errors=True)
    except configobj.ConfigObjError:
        print "ERROR FOR CONFIG FILE 'user_conf':"
        raise
    config.merge(user)

    #This is config file validation
    fdict = {'check_list_of_list_of_integers': list_of_list_of_integers}
    validator = validate.Validator(fdict)

    results = config.validate(validator, preserve_errors=True)
    if results != True:
        for entry in configobj.flatten_errors(config, results):
            # each entry is a tuple
            section_list, key, error = entry
            if key is not None:
                section_list.append(key)
            else:
                section_list.append('[missing section]')
            section_string = ', '.join(section_list)
            if error == False:
                error = 'Missing value or section.'
            print section_string, ' = ', error

        sys.exit(1)

    return config

ATUALIZAÇÃO 16 de dezembro de 2015: algo como o YAML é provavelmente uma solução geral melhor. Eu recomendo que, em vez de ConfigObj. Por uma coisa, é mais flexível.

    
por 24.08.2012 / 02:34