Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I want to make a shell script that updates a config file.

The file present on the server has information, like various IP addresses.

The new file has more code, from new configs added to the system, I want to be able to add these new configurations to the server file without changing what's already configured there

example:

server file

[config]
ip=127.0.0.1
port=22

new file

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

I want the resulting file to be

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

How would be a good way to do that? I don't want to rely on line position and such, because the actual config files are quite large and more lines could have been added to the server file.

I've tried to make a diff from the files and apply the patch, but it didn't work.

Thanks for any help.

share|improve this question
1  
This may require some config-file-specific logic. It might be worth investigating a framework such as ucf. –  Gilles Aug 23 '12 at 20:49
add comment

3 Answers 3

As Gilles says, you'll need to use a tool that knows about the format of your config files. For your particular example, you can use a short Python script that uses the built-in ConfigParser module.

  • First, let's say your original configuration file on the server is original.cfg:

    [config]
    ip=127.0.0.1
    port=22
    
  • Now put your changes in a new file called update.cfg:

    [config]
    user=root
    

    This file should have new or changed entries listed under the section heading where you would like them to go.

  • Then run a script like this one:

    #!/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)
    

There are, of course, plenty of variations on how to do this. For example, the config changes could be coded directly into the Python script rather than read from a separate update.cfg file. This should give you a good base to get started, though.

share|improve this answer
add comment

Parse them into two separate datasets with whatever program you prefer. I would use common lisp and store each config file as a nested list of lists.

And then use language tools to do the merging/patching/union/set-difference/etc. At a high level, for a one side diff, the code might look like:

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

And then write out the resulting dataset to a file.

share|improve this answer
    
Interesting. Do you have example CL code? –  Faheem Mitha Aug 24 '12 at 0:51
    
Not anything that will compile, other than the sketch above. I figured if you went this route, you wouldn't use common lisp, so I wanted to just convey the idea. –  Clayton Stanley Aug 24 '12 at 0:55
    
Ok. Just to be clear, I'm not the OP, and do know CL, so I'm interested. I see there is something like py-configparser, which might be able to do similar things to the configparser module. –  Faheem Mitha Aug 24 '12 at 1:02
    
Yeah; the python solutions here are much more practical. But if you can get to the point in python where you are expressing set-diff, union, intersection, etc., then I think you have a more general case of this problem solved. Which might be useful. –  Clayton Stanley Aug 24 '12 at 1:04
    
Ah; you said you do know CL; yeah there are XML parser packages; I've messed around with Closure XML, but this config isn't XML. I've found JSON parsers for CL, but this config isn't JSON either. I dunno' if there's a lisp package to parse this one. I'd have to do a bit of research; and if not, I'd just use python :) –  Clayton Stanley Aug 24 '12 at 1:11
show 2 more comments

I use the ConfigObj Python library for this. If you do this, it does require writing Python to interface with the config files. I typically have a default_conf file which has default values, and I can selectively override these settings with some other file, say user_conf

Here is the code I am currently using as an example. The second half of the function is validation, which is important, but you can ignore it to a first approximation. For usage you do

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

In your case, I guess the server file is the one that overrides, though more usually it is the user file which overrides the system file. That is certainly the user convention in Unix-type systems.

To write a ConfigObj object to file, to a first approximation you do (if config is your object as in the script below)

config.filename = filename
config.write()

So you could modify the function below to write to a file instead of returning the config object if you want. See Writing a Config File for a little more detail.

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
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.