2

I want to replace a line in a file with multiple lines. I know I can use \n in the sed replace, but that is rather ugly. I was hoping to HEARDOCs.

So I can do this to replace the line with multiple lines:

$ cat sedtest
DINGO=bingo

$ sed -i -e "s/^DINGO.*$/# added by $(whoami) on $(date)\nDINGO=howdy/" sedtest

$ cat sedtest
# added by user on Sun Feb  3 08:55:44 EST 2019
DINGO=howdy

In the command I want to put the replacement in new lines so it's easier to read/understand. So far I have been using HEREDOCs when I want to add new lines to a file:

CAT << EOF | sudo tee -a file1 file2 file3
line one
line two
line three
EOF

And this has worked well for appending/adding. Is it possible to do something similar but instead use the output as the replacement in sed or is there some other way to do what I'm looking for?

IMTheNachoMan
  • 5,343
  • 5
  • 40
  • 89
  • Please add sample input and your desired output for that sample input to your question. – Cyrus Feb 03 '19 at 14:06
  • The first code block _does_ have a sample input and output using `\n`. I'm hoping to achieve the same output to the input... – IMTheNachoMan Feb 03 '19 at 14:08

2 Answers2

1

Is this what you're trying to do?

$ awk 'NR==FNR{new=(NR>1?new ORS:"") $0;next} $0=="DINGO=bingo"{$0=new} 1' - file <<!
# added by $(whoami) on $(date)
DINGO=howdy
!
# added by user on Sun, Feb  3, 2019  8:50:41 AM
DINGO=howdy

Note that the above is using literal string operations so it'll work for any characters in the old or new strings unlike your sed script which would fail given /s or any ERE regexp character or capture groups or backreferences or... in the input (see Is it possible to escape regex metacharacters reliably with sed for details).

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • will this replace the file in-place? – IMTheNachoMan Feb 03 '19 at 14:52
  • No, just add `> tmp && mv tmp file` to the end of the script to do the same kind of pseudo-inplace editing with a tmp file that `sed -i` does internally, or if you have GNU awk add `-i inplace` at the start just like you use `-i` with GNU sed. – Ed Morton Feb 03 '19 at 14:54
  • Thanks. I will test this out after I figure out what it's doing. I try to understand commands before executing them. Or, would you be willing to break down what everything in the `awk` command does? – IMTheNachoMan Feb 03 '19 at 16:25
  • 1
    You're welcome. No, but I'd be willing to answer questions after you've had a glance at the awk man pages (you'll learn much more from that than by me spoon-feeding you just this answer). – Ed Morton Feb 03 '19 at 16:37
  • I know what the individual parts like `NR`, `FNR`, the conditional/ternary `NR > 1 ? new ORS : ""`, etc... mean but not sure what it means put together. For the first file, and only the first file, set `new` to `new ORS` (??) if `NR>1`, then as it goes through `file`, if the line matches `"DINGO=bingo"`, replace it with `new`... – IMTheNachoMan Feb 03 '19 at 19:08
  • Wait, so the ` - ` is the first file and that is the heredoc > so `new` gets set to the heredoc, and then it skips everything else in the first file, and for the second file it replaces that matching line with `new`? Is that correct? – IMTheNachoMan Feb 03 '19 at 19:11
  • Pretty close but there's nothing else in the first "file" (i.e. stdin represented by `-` which is taken from the here doc) to skip. The `next` command just ensures that when `NR==FNR` (i.e. awk is reading that first "file") the subsequent part that does the search/replace isn't executed. That subsequent part is only executed when `NR==FNR` is **not** true, i.e. when the 2nd file (named `file` in my example) is being read. You can always add `print`s to see what's being read, what the `new` variable is being set to, etc. – Ed Morton Feb 03 '19 at 19:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/187814/discussion-between-imthenachoman-and-ed-morton). – IMTheNachoMan Feb 03 '19 at 19:20
1

This might work for you (GNU sed):

cat <<! | sed -i -e '/^DINGO/r /dev/stdin' -e '//d' file
# added by $(whoami) on $(date)
DINGO=howdy
!

This replaces lines starting DINGO with the here-document which is piped to stdin as file within the sed command.

An alternative:

cat <<! | sed -i -e '/^DINGO/e cat /dev/stdin' -e 's/bingo/howdy' file
# added by $(whoami) on $(date)
!

N.B. In the alternative solution the here-doc will only be read once!

potong
  • 55,640
  • 6
  • 51
  • 83
  • This is great! What does `/r` and `//d` mean? I can't find references to it in the man pages. – IMTheNachoMan Feb 03 '19 at 19:02
  • @IMTheNachoMan `/r file` reads the contents of `file` and appends it following the current line. `//d` repeats the last regexp and deletes that line. To insert a file, see my alternative, this allows the current line to be amended. – potong Feb 04 '19 at 09:43