2

I'm trying to use sed to replace [databases] in pgbouncer.ini with

[databases]
db = host=localhost port=5432 dbname=db user=r00t password=xyz

This is my bash script:

CR=$(printf '\r')
sed "s/\[databases\]\$/\[databases\]$CR db = host=localhost port=5432 dbname=db user=r00t password=xyz/" pg.ini

Unfortunatelly I get only this shown instead:

db = host=localhost port=5432 dbname=db user=r00t password=xyz

UPDATE:

Variables would cause a problem in the accepted answer. I get the error:

sed: -e expression #1, char 127: extra characters after command

sed -i "/\[databases\]/a\
main_db = host=${MAIN_DB_LOC} port=5432 dbname=${MAIN_DB} user=r00t password=${MAIN_DB_PASSWORD}
audit_db = host=${AUDIT_DB_LOC} port=5432 dbname=${AUDIT_DB} user=r00t password=${AUDIT_DB_PASSWORD}
lat_lng_db = host=${LAT_LNG_DB_LOC} port=5432 dbname=${LAT_LNG_DB} user=r00t password=${AUDIT_DB_PASSWORD}
" /etc/pgbouncer/pgbouncer.ini
Houman
  • 64,245
  • 87
  • 278
  • 460

3 Answers3

1

Instead of CR, use the newline, available as \n in sed (LF on Unix):

$ sed 's/\[databases\]$/&\ndb = host=localhost port=5432 dbname=db user=r00t password=xyz/' pg.ini

Also, end-of-line anchor $ doesn't need to be escaped (if used under single quotes), and you can use & to re-use the matched string ([databases]) in your replacement string.


To use the port variable:

$ port=5432
$ sed 's/\[databases\]$/&\ndb = host=localhost port='"$port"' dbname=db user=r00t password=xyz/' pg.ini

(Note we need to use double quotes to expand the $port, that's why we concatenate single-quoted and double-quoted sed substrings, e.g. 'x'"y"'z'.)

randomir
  • 17,989
  • 1
  • 40
  • 55
  • This is a good solution if the port is a variable instead of hardcoded. I might actually accept this one as the answer. – Houman Jun 18 '18 at 16:57
  • Yes, indeed, I think this is still a good solution to keep everything in one line. The modified accepted answer has one advantage of keeping each line separate for easier readability. Thank you – Houman Jun 19 '18 at 08:34
0

You can use a append command like this:

sed -i '/\[databases\]/a\
db = host=localhost port=5432 dbname=db user=r00t password=xyz
' file

Here is how you can use port as variable:

port='5432'

sed -i '/\[databases\]/a\
db = host=localhost port='"$port"' dbname=db user=r00t password=xyz
' file

Or with a long set of variables as shown in question you may use this sed:

sed -i "/\\[databases\\]/a\\
main_db = host=${MAIN_DB_LOC} port=5432 dbname=${MAIN_DB} user=r00t password=${MAIN_DB_PASSWORD}\\
audit_db = host=${AUDIT_DB_LOC} port=5432 dbname=${AUDIT_DB} user=r00t password=${AUDIT_DB_PASSWORD}\\
lat_lng_db = host=${LAT_LNG_DB_LOC} port=5432 dbname=${LAT_LNG_DB} user=r00t password=${AUDIT_DB_PASSWORD}\\
" file

Note use \\ instead of \ due to presence of double quotes in sed command.


Alternatively, you can use r command of sed to place a file's content in output.

# create a temporary file with the text to be placed after [databases]
echo 'db = host=localhost port=5432 dbname=db user=r00t password=xyz' > file.tmp

# Copy the contents of file to the standard output after search string
sed -i.bak '/\[databases\]/r file.tmp' file
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Unfortunately, this solution won't work anymore if the port has to be a variable rather than hardcoded. If you use " " instead of ' ', sed would throw an error. – Houman Jun 18 '18 at 16:56
  • Updated answer with an example of using variable – anubhava Jun 18 '18 at 17:18
0

sed is for simple subsitutions on individual lines, that is all. For anything else you should be using awk:

awk '{print} $0=="[databases]"{print "db = host=localhost port=5432 dbname=db user=r00t password=xyz"}' file

The above will work with any awk in any shell on any UNIX system and will not fail when your target string contains regexp metacharacters or delimiters (e.g. / be default in sed) nor will it fail when your additional string contains backreferences (e.g. & or \1). Thats because it's using strictly string operations which sed does not support.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • sed can be used for much more than simple substitutions! What's more, if you need to use metacharacters in sed you can, you just need to quote them, just like you'd need to quote any `"` or `\` characters in your awk example; or you can use a sed command like `a` which is a "string operation" just as much as your awk example. – psmears Oct 12 '17 at 13:25
  • 1
    Yes it can and it should not be. When you do so you're using constructs that became obsolete in the mid-1970s when awk was invented and the result is complicated, inefficient and usually non-portable. When you say "quote" you mean "escape" and to do so is cumbersome and non-trivial (see https://stackoverflow.com/a/29626460/1745001), and usually pointless when you can just use a tool that supports strings instead of having to hack your regexp to try to stop sed from treating it as such. Use of sed for more than `s/old/new/` today is just for mental exercise. – Ed Morton Oct 12 '17 at 13:31
  • Character quoting is a [particular case](https://en.wikipedia.org/wiki/Escape_character) of escaping; both are correct in this context, "quoting" is just slightly more specific. Yeah, I'm not going to recommend anyone *should* write big programs in sed - that would be crazy! - but saying "that is all" it can do is misleading because it gives the impression that that's all you *can* do with it, as opposed to all you *should* do with it. And you always have to be careful with quoting, whether you're using sed, awk, SQL or anything else... – psmears Oct 12 '17 at 13:48
  • 1
    I didn't say that is all it can do, I said that is all it is for. You CAN dig a trench with a teaspoon but that's not what a teaspoon is FOR. There is a vast difference between having to quote string delimiters within a literal string vs having to escape every regexp and backreference metacharacter or string in the appropriate way for each context. Show me any sed script that's not simply s/old/new and I'll show you a better way to do it, with "better" being any/all of clarity, performance, portability, memory, etc. If you doubt that, post a question... – Ed Morton Oct 12 '17 at 15:47
  • Yes, you said that is all it is "for", implying that that was its authors' intention, which may lead the reader to believe that they didn't put in any features to do anything else - which is unfortunately not the case. It would make your intent clearer to say something along the lines of "sed is only to be recommended for (simple stuff)" or "It is inadvisable to use sed for ...". And as I've said, I'm not recommending people write complex stuff in sed, no argument there! – psmears Oct 12 '17 at 15:59
  • as simple as that ++ – Rahul Verma Oct 27 '17 at 17:55