How to add a line in sed if not match is found [duplicate]

Just keep it simple :)

grep + echo should suffice:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar
  • -q be quiet
  • -x match the whole line
  • -F pattern is a plain string
  • https://linux.die.net/man/1/grep

Edit: incorporated @cerin and @thijs-wouters suggestions.


Try this:

grep -q '^option' file && sed -i 's/^option.*/option=value/' file || echo 'option=value' >> file

This would be a clean, readable and reusable solution using grep and echo to add a line to a file only if it doesn't already exist:

LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

If you need to match the whole line use grep -xqF

Add -s to ignore errors when the file does not exist, creating a new file with just that line.


Using sed, the simplest syntax:

sed \
    -e '/^\(option=\).*/{s//\1value/;:a;n;ba;q}' \
    -e '$aoption=value' filename

This would replace the parameter if it exists, else would add it to the bottom of the file.

Use the -i option if you want to edit the file in-place.


If you want to accept and keep white spaces, and in addition to remove the comment, if the line already exists, but is commented out, write:

sed -i \
    -e '/^#\?\(\s*option\s*=\s*\).*/{s//\1value/;:a;n;ba;q}' \
    -e '$aoption=value' filename

Please note that neither option nor value must contain a slash /, or you will have to escape it to \/.


To use bash-variables $option and $value, you could write:

sed -i \
    -e '/^#\?\(\s*'${option//\//\\/}'\s*=\s*\).*/{s//\1'${value//\//\\/}'/;:a;n;ba;q}' \
    -e '$a'${option//\//\\/}'='${value//\//\\/} filename

The bash expression ${option//\//\\/} quotes slashes, it replaces all / with \/.

Note: Just trapped into a problem. In bash you may quote "${option//\//\\/}", but in the sh of busybox, this does not work, so you should avoid the quotes, at least in non-bourne-shells.


All combined in a bash function:

# call option with parameters: $1=name $2=value $3=file
function option() {
    name=${1//\//\\/}
    value=${2//\//\\/}
    sed -i \
        -e '/^#\?\(\s*'"${name}"'\s*=\s*\).*/{s//\1'"${value}"'/;:a;n;ba;q}' \
        -e '$a'"${name}"'='"${value}" $3
}

Explanation:

  • /^\(option=\).*/: Match lines that start with option= and (.*) ignore everything after the =. The \(\) encloses the part we will reuse as \1later.
  • /^#?(\s*'"${option//////}"'\s*=\s*).*/: Ignore commented out code with # at the begin of line. \? means «optional». The comment will be removed, because it is outside of the copied part in \(\). \s* means «any number of white spaces» (space, tabulator). White spaces are copied, since they are within \(\), so you do not lose formatting.
  • /^\(option=\).*/{…}: If matches a line /…/, then execute the next command. Command to execute is not a single command, but a block {…}.
  • s//…/: Search and replace. Since the search term is empty //, it applies to the last match, which was /^\(option=\).*/.
  • s//\1value/: Replace the last match with everything in (…), referenced by \1and the textvalue`
  • :a;n;ba;q: Set label a, then read next line n, then branch b (or goto) back to label a, that means: read all lines up to the end of file, so after the first match, just fetch all following lines without further processing. Then q quit and therefore ignore everything else.
  • $aoption=value: At the end of file $, append a the text option=value

More information on sed and a command overview is on my blog:

  • https://marc.wäckerlin.ch/computer/stream-editor-sed-overview-and-reference