Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appending a line to a file only if it does not already exist

People also ask

How can I append some lines in existing file using which command?

Append Text Using >> Operator Alternatively, you can use the printf command (do not forget to use \n character to add the next line). You can also use the cat command to concatenate text from one or more files and append it to another file.

How do you append one file to another file without overwriting the content of the file in Linux?

In this method, the >> operator can be used to append text to the end of a file without overwriting its content. Similarly, if the file was not found, the command creates a new file. As you can see, using the >> operator, the text was added to the end of the file and did not overwrite the file content.

How do you append without a new line?

Assuming that the file does not already end in a newline and you simply want to append some more text without adding one, you can use the -n argument, e.g. However, some UNIX systems do not provide this option; if that is the case you can use printf , e.g. Do not print the trailing newline character.

Which command is correct to append content in existing file?

Using the >> operator will append data at the end of the file, while using the > will overwrite the contents of the file if already existing.


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: i 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

If writing to a protected file, @drAlberT and @rubo77 's answers might not work for you since one can't sudo >>. A similarly simple solution, then, would be to use tee --append (or, on MacOS, tee -a):

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