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 withoption=
and (.*
) ignore everything after the=
. The\(
…\)
encloses the part we will reuse as\1
later. - /^#?(\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 text
value` -
:a;n;ba;q
: Set labela
, then read next linen
, then branchb
(or goto) back to labela
, that means: read all lines up to the end of file, so after the first match, just fetch all following lines without further processing. Thenq
quit and therefore ignore everything else. -
$aoption=value
: At the end of file$
, appenda
the textoption=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