4

I need to add a line to the beginning of a file. The line in question is

\def\submit{}

I've got a semi-working solution using sed. I don't know sed, but got it from somewhere on the net, but it doesn't work quite right, because it inserts an unwanted space at the beginning of the line. While this doesn't really matter, I figure I might as well do this right.

sed -i '1i\ \\\def\\\submit{}' 'dirname/filename'

It seems from my reading that all those extra backslashes are required to escape the shell. Other solutions are also welcome, but I'd like a comparably compact one liner if possible. Thanks.

This question, Inserting text at the beginning of a file with sed via the terminal in Linux is similar, but doesn't help me debug my expression.

EDIT: I've accepted @Jonathan's answer, because it explains what was wrong with my previous approach. However, I've also added an answer.

Faheem Mitha
  • 35,108
  • Interesting edit. Is there a way to keep the question closed but modify the duplicate it points to? – rahmu Jan 18 '14 at 22:37
  • @rahmu Don't see how. The duplicate is not really a duplicate imo. – Faheem Mitha Jan 18 '14 at 22:41
  • Actually, if you want to use 1i you need GNU sed. And unless you want a leading space on the first line, the sed command should be: 1i\\\def\\submit{}. The first backslash needs to be tripled because the first backslash character is actually part of the command name, which is i\. – peterph Jan 18 '14 at 23:39
  • @peterph This does not work for me - I tried sed -i '1i\\\def\\submit{}\' 'dir1/foo'. Feel free to submit your own solution. I got \\def\\submit{}. – Faheem Mitha Jan 19 '14 at 15:57
  • Well, I suggested 1i\\\def\\submit{}, you tried 1i\\\def\\submit{}\ - don't be surprised you got different result. – peterph Jan 19 '14 at 20:02
  • @peterph: Yes, sorry about that. Actually, what you suggest is the same as Jonathan's suggestion below. Except that you have one less slash before the submit, which however, doesn't make any difference to the result - both versions work. I guess I should stop cargo-culting and understand what sed actually does. – Faheem Mitha Jan 19 '14 at 20:52
  • Escaping backslashes is usually a tricky thing. Anyway, I've summarized my findings in a new answer (editing any existing one didn't seem appropriate in this case) - it might clarify things a bit. – peterph Jan 19 '14 at 21:39

4 Answers4

5

sed is for editing streams -- a file is not a stream. Use a program that is meant for this purpose, likeed or ex. The -i option to sed is not only not portable, it will also break any symlinks to your file, since it essentially deletes it and recreates it, which is pointless.

ed -s [file] << EOF
0a
\\def\\submit{}
.
w
EOF
Chris Down
  • 125,559
  • 25
  • 270
  • 266
  • 1
    jw013 suggests that you mean hard links, not symlinks – Michael Mrozek Sep 21 '11 at 20:40
  • 1
    @Michael -- I don't. Try creating a file, then creating a symlink to that file, then passing the symlink as the file for sed -i to operate on. – Chris Down Sep 21 '11 at 20:49
  • @ChrisDown Just saw this again. You are indeed correct about -i removing symlinks, although GNU sed provides the --follow-symlinks option for that case. Of course all of this is rendered moot by the more important point that sed -i is non-portable and should be avoided anyways. – jw013 Mar 22 '12 at 16:03
4

I don't have access to sed right now, so I'm not entirely sure this is correct, but I think you just need to remove the first backslash and the space following it.

sed -i '1i\\\def\\\submit{}' 'dirname/filename'

Try it and see if that works.

Jonathan
  • 1,304
2

I was able to get a working version of this by using an answer to: "How can I prepend a tag to the beginning of several files?".

I was not previously aware of this question. The solution is:

sed -i '1s/^/\\\def\\\submit{}\n/' 'dirname/filename'

BTW, I think: "How can I prepend a tag to the beginning of several files?" would be a better question to reference than the: "Inserting text at the beginning of a file with sed via the terminal in Linux" which this question was linked to. It has a lot more answers.

Faheem Mitha
  • 35,108
0

First: the escaping backslashes are not because of the shell - the string is single quoted. Rather it is because of sed itself.

Secondly: Chris Down's answer has some merit - don't use hammer instead of screwdriver. On the other hand, portability and symlinks are not always an issue, if that is the case, both

sed '1i\\\def\\submit{}'

and

sed '1i\\def\submit{}\'

Should do what you need. I wasn't able to find out why the trailing backslash in the latter form turns off escaping in the string, but it seems to work like that, at least in the GNU sed when the commands are put on a command line. When used in a script, only the first form seems to work (because trailing backslash is used to denote end of line which is followed by another one):

#!/bin/sed -f 
1i\
\\def\\submit{}              
peterph
  • 30,838