When modifying config files from the command line, I often want to find the setting in the config file and modify that line if that setting exists.   If that setting doesn't exist, I want to add it to the end of the file.I end up doing something like:if [ `grep -c '^setting=' example.conf` == 0 ]then     echo setting=value >> example.confelse     sed -i 's/^setting=.*/setting=value/g' example.conffiWhich seems like an awful lot of code for something so simple.   This doesn't even do basic things like check that the config file already ends in a new line before appending to it.   Surely there is a utility that does this, or a simpler command that I can use.
Change a value in a config file, or add the setting if it doesn't exist?
shell script;command line;scripting;configuration
Here is a confset Perl script that I just wrote that I'm going to put in my path:Can work with multiple files in a single invocationCan modify multiple config values in each file in a single invocationSeparator can be specified (with --separator)Option to be liberal about white space around namesUsage: confset <options> name1=value1 name2=value2 file1.conf file2.confOptions:  -s --separator <value>        What comes between names and values (default =)  -w --whitespace  <true|false> Allow space around names and values (default false)So to the handle the case I outlined in the question, I would call it with: confset example.conf setting=valueHere is the script:#!/usr/bin/perluse strict;my $scriptname = $0;my $separator = '=';my $whitespace = 0;my @files = ();my @namevalues = ();# read in the command line argumentsfor (my $i=0; $i<scalar(@ARGV); $i++){    my $arg = @ARGV[$i];    if ($arg =~ /^-/){        &printHelp(*STDOUT, 0) if ($arg eq -h or $arg eq --help);        &printHelp(*STDERR, 1) if ($i+1 >= scalar(@ARGV));        my $opt = @ARGV[++$i];        if ($arg eq -s or $arg eq --separator){            $separator = $opt;        } elsif ($arg eq -w or $arg eq --whitespace){            $whitespace = 0;            $whitespace = 1 if ($opt =~ /1|t|y/);        } else {            &printHelp(*STDERR, 1);        }    } elsif ( -e $arg){        push(@files, $arg);    } else {        push(@namevalues, $arg);    }}# check the validity of the command line argumentsif (scalar(@files) == 0){    print STDERR ERROR: No files specified\n;    printHelp(*STDERR, 1);}if (scalar(@namevalues) == 0){    print STDERR ERROR: No name value pairs specified\n;    printHelp(*STDERR, 1);}my $names = {};foreach my $namevalue (@namevalues){    my ($name, $value) = &splitnv($namevalue);    if ($name){        $names->{$name} = {value,$value,replaced,0};    } else {        print STDERR ERROR: Argument not a file and contains no separator: $namevalue\n;        printHelp(*STDERR, 1);    }}# Do the modification to each conf fileforeach my $file (@files){    # read in the entire file into memory    my $contents = ;    open FILE, $file or die $!;    while (my $line = <FILE>){        chomp $line;        my ($name, $value) = &splitnv($line);        # set matching lines to their new value        if ($names->{$name}){            $line = $name . $separator . $names->{$name}->{value};            $names->{$name}->{replaced} = 1;        }        $contents .= $line\n;    }    close FILE or die $!;    # add any new lines that didn't already get set    foreach my $name (keys %$names){        if (!$names->{$name}->{replaced}){            $contents .= $name . $separator . $names->{$name}->{value}.\n;        }        # reset for next file        $names->{$name}->{replaced} = 0;    }    # overwrite the file    open FILE, >$file or die $!;    print FILE $contents;    close FILE or die $!;}# Print help message to the specified stream and exit with the specified valuesub printHelp(){    my ($stream, $exit) = @_;    print $stream Usage: $scriptname <options> name1=value1 name2=value2 file1.conf file2.conf\n;    print $stream Options:\n;    print $stream   -s --separator <value>        What comes between names and values (default =)\n;    print $stream   -w --whitespace  <true|false> Allow space around names and values (default false)\n;    exit $exit;}# Split a string into a name and value using the global separatorsub splitnv(){    my ($str) = @_;    my $ind = index($str, $separator);    return (0,0) if ($ind < 0);    my $name = substr($str, 0, $ind);    my $value = substr($str, $ind+length($separator));    $name =~ s/(^[ \t])*|([ \t])*$//g if ($whitespace);    return ($name, $value);}