5

I have a command that prints a single line. I want to add/pipe this line to a file, just above its last line.

my_cmd | sed -i '$i' test

I just find an empty line in the correct place, above the last line. I notice that when I add any string as '$i foo', the "foo" gets printed in the correct place, but I want the piped line to be printed.

How can I use STDIN instead of "foo"?

Tarek Eldeeb
  • 588
  • 2
  • 6
  • 24

3 Answers3

13

this should do the trick:

 sed -i "\$i $(cmd)" file

test:

kent$  cat f
1
2
3
4
5

kent$  sed -i "\$i $(date)" f

kent$  cat f
1
2
3
4
Tue Sep 30 14:10:02 CEST 2014
5
Kent
  • 189,393
  • 32
  • 233
  • 301
  • Thanks Kent, your solution basically works, but if the cmd generates white spaces (or tabs), they do not show. Please retry your command with $(date|sed 's/^/\t/'). Now all indentations are lost! – Tarek Eldeeb Sep 30 '14 at 12:26
  • 1
    @TarekEldeeb now? `sed -i "\$i \ $(date|sed 's/^/\t/')" f` – Kent Sep 30 '14 at 12:39
  • try maybe `sed -i "\$i \\\n$( cmd | sed 's/$/\\/' )" file` (cannot test, no GNU sed available) – NeronLeVelu Sep 30 '14 at 12:43
  • @Kent Perfect, but I do not understand how your last edit made sed consider leading white spaces?? – Tarek Eldeeb Sep 30 '14 at 12:43
  • @NeronLeVelu this only adds a proceeding "n". I replaced it by a "t", same result. – Tarek Eldeeb Sep 30 '14 at 12:46
  • @Kent, your solutions adds an extra space. This one works perfect: `sed -i "\$i\\$(date|sed 's/^/\t/')" file` – Tarek Eldeeb Sep 30 '14 at 12:57
  • @TarekEldeeb oh, yes, yours is correct, the `\` is for keeping the whitespaces in output. for detailed info, `info sed` then search `a\'` – Kent Sep 30 '14 at 12:59
10

Instead of passing your output to sed via pipe, you can use command substitution instead:

$ cat f
First line
Second line
Third line

$ sed -i '$i'"$(echo 'Hello World')" f
$ cat f
First line
Second line
Hello World
Third line

So in your case you can use:

sed -i '$i'"$(my_cmd)" test
Josh Jolly
  • 11,258
  • 2
  • 39
  • 55
  • Can you please explain how `sed -i '$i foo' file` adds foo before the last line. Basically i dont understand what `$i` means. – Fazlin Sep 30 '14 at 12:16
  • 8
    `$` means the last line, and `i` is a command that means "insert the following text before the preceding line number". – Josh Jolly Sep 30 '14 at 12:21
3

The other answers should work too.

Here is another approach, which uses a syntax similar to your code snippet and is free from shell injection exploits.

$ seq 1 5 > test.input
$ echo hello/world | sed '${x;s/.*/cat/e;p;x}' test.input
1
2
3
4
hello/world
5

PRO: This solution is protected from shell injection exploits.

CON: This is a GNU sed specific answer. So it may not be portable.

anishsane
  • 20,270
  • 5
  • 40
  • 73
  • Replace `echo hello/world` by `your_cmd`. All indentations & special characters should be retained. – anishsane Sep 30 '14 at 12:45
  • This seems to answer my exact question, but unfortunately I'm on cygwin! My sed complains as: sed: bad option in substitution expression – Tarek Eldeeb Sep 30 '14 at 12:51
  • the `sed` on my cygwin box is also GNU sed & this solution worked for me on cygwin too. cygwin too old? – anishsane Sep 30 '14 at 13:08
  • I use MobaXterm, I though it includes a full cygwin environment, but turned to be not. It has a minimalist sed, that's found in BusyBox v1.19.2 (2012-06-12 22:17:17 ) multi-call binary. – Tarek Eldeeb Sep 30 '14 at 13:33
  • aah! busybox! that's indeed minimal! – anishsane Sep 30 '14 at 14:15
  • If you get busybox and checked its sed source code, you will find the following on line 3:: sed.c - very minimalist version of sed. Have a good day! – Tarek Eldeeb Oct 01 '14 at 14:25