4

I need to insert some text into an HTML file before the end of the HEAD section.

I'm trying:

sed -i "s#</head>#<style> @page { prince-shrink-to-fit: auto }
.repository-with-sidebar.with-full-navigation .repository-content 
{width: 950px ! important;} </style>\n</head>#" ${1}"/"${2}"/"${i}.html

I'm getting an error:

sed: 1: command a expects \ followed by text

I just don't see my error. I even tried using single quotes around the style.

The exact text I want to insert before the end of the HEAD section is:

<style>
@page { prince-shrink-to-fit: auto }
.repository-with-sidebar.with-full-navigation .repository-content 
{width: 950px ! important;}
</style>

Edit: Here is the exact command:

sed -i "s#</head>#<style> @page { prince-shrink-to-fit: auto } .repository-with-sidebar.with-full-navigation .repository-content {width: 950px ! important;} </style>\n</head>#" ${1}"/"${2}"/"${i}.html

Edit 2: In an effort to try and make sure I was formatting the command correctly I am trying this too:

strToInsert='<style> @page { prince-shrink-to-fit: auto } .repository-with-sidebar.with-full-navigation .repository-content {width: 950px ! important;} </style>'

sed -i "s#</head>#$strToInsert\n</head>#" ${1}"/"${2}"/"${i}.html

This makes me realize it might be a problem with the text I am inserting.

  • 1) Are those newlines literally inside the quotes? 2) Are you on BSD or OSX? – Michael Homer Apr 11 '15 at 21:10
  • @MichaelHomer no, sorry I just did that for easy SO formatting. I am on OS X technically – GameDeveloper Apr 11 '15 at 21:12
  • 1
    Please provide the command exactly as you're using it. I've just written an answer (and now abandoned it) about your multiline sed. – roaima Apr 11 '15 at 21:14
  • 1
    If you are on OS X, why on earth have you tagged it "linux"? – Michael Homer Apr 11 '15 at 21:23
  • 2
    That is specifically the error message from OS X sed, and it does not occur at all on Ubuntu's default sed. You will get better answers to your questions if you provide accurate information. – Michael Homer Apr 11 '15 at 21:31
4

In-place sed requires making a backup file during the process. The -i option on Apple's sed requires an extension argument (for the backup file it creates) and consumes the next argument. That means you're telling it you want it to make a backup file with the extension "#s</head>#...".

The error means it thinks you're referring to the append command. I'm going to guess that the value of $1 starts with an a. Because -i ate the preceding argument sed thinks that this is the script to run, rather than the path to the file to change, and you get errors accordingly. This is exactly the error you would get with Apple sed and an invalid a command:

$ sed a
sed: 1: "a": command a expects \ followed by text

Provide an extension argument to -i in your sed command:

sed -i.bak "s#</head>#<style> @page { prince-shrink-to-fit: auto } .repository-with-sidebar.with-full-navigation .repository-content {width: 50px ! important;} </style>\n</head>#" "${1}/${2}/${i}.html"

and things will work (although note that \n is not a newline escape). If you use single quotes you can even take the space out of "!important".

The above will work on GNU sed as well, but note that -i is not a POSIX option and is not generally portable.

It is often recommended not to use in-place modification anyway, and to handle working with a copy of the file explicitly yourself. That is up to you, though.


You should quote the variable expansions ${1}, etc. Quoting only the slashes is pointless, because those are always literal.

  • Thank you, great answer. I understand. I should have looked at the man page better. I totally missed this. – GameDeveloper Apr 11 '15 at 21:33
  • @GameDeveloper If this answer solved your issue, please take a moment and accept it by clicking on the check mark to the left. That will mark the question as answered and is the way thanks are expressed on the Stack Exchange sites. – terdon Apr 12 '15 at 9:07
4

My apologies, but reading your statement The exact text I want to insert after end of the HEAD section is... led me initially to believe we were talking about appending text to file section rather than inserting before - so I wrote that answer first. After rereading the question, and more closely studying your example sed command - I think I understand this really is about inserting text before a match. So that answer is now here as well - you'll find instructions below for doing either thing.

To safely insert or append an arbitrary string to the head/tail of a line w/ sed you have a few options, each more complex than the last.

The first - and the most simple of all - is the read command. It requires zero escaping. For example, if the string you wanted to append was located in the file ./append you could just do:

sed '\|</head>|r ./append' <infile >outfile

Or to insert it ahead of the match you can match it one cycle before it is printed with a little lookahead like:

sed -e '$!N;\|\n</head>|r ./insert' -e 'P;D' <infile >outfile

But read is spec'd to write to stdout - and I'm not sure how that would work w/ the -inplace option (which is mostly just an ugly hack in my opinion, anyway). I assume that sed should do the same thing it does w/ print and redirect stdout to its named -inplace edit file.

Another option is to use the append command - for appending that is. To do this safely you need only escape \newlines and backslashes. You might do this like:

printf %s\\n "$str_to_append" |
sed 's|\\|&&|g;$!s|$|\\|;1i\
    \\|</head>|a\\' |
sed -f - infile >outfile

append is spec'd to behave as read does, though, and so I am again unsure how it might behave in an -in-place hack.

If you replace a\\ above w/ i\\ it will insert the text to stdout (or named -inplace edit-file - if that works for your implementation) before the pattern space is written out.

If you like extra work, (or, in the only practical use I can think of, you're scheduling multiple reads/appends per line in a certain order) it can be used for inserts as well in the same way I did with read before:

printf %s\\n "$str_to_append" |
sed 's|\\|&&|g;$!s|$|\\|;1i\
    \\|\\n</head>|a\\' |
sed -e '$!N' -f - -e 'P;D' infile >outfile

The last I'll offer is nearly identical except that you must also escape & and delimiter (which is typically /). It could work like this:

printf %s\\n "$str_to_append" |
sed 's|[\/&]|\\&|g
   $!s|$|\\|;$s|$|/|
     1i\
     /<\\/head>/s/^/\\' |
sed -f - infile >outfile

That will effect an insertion - you would just swap the ^ with a $ if you wished to append the string that way.

I'm fairly confident that will work whether you add the -i option or not.

One last note, though - rather than using -i, you might also do:

sed '$d;\|</head>|r ./append' <<IN >infile
$(   cat <infile; echo .)
IN

...or, for an insert:

sed -e '$d;N;\|\n</head>|r ./insert' -e 'P;D' <<IN >infile
$(   cat <infile; echo .)
IN

...which will get your infile edited in-place without any unintended permissions hiccups as -i might effect.

  • That last is a really cute trick. – Michael Homer Apr 11 '15 at 22:33
  • @MichaelHomer - I just realized this was about inserting text before the line in question. – mikeserv Apr 11 '15 at 22:34
  • @don_crissti - you're gettting really good w/ sed. Thanks for showing others how, too. You might like this - I found it rather self-affirming anyway. A new thing I noticed about sed the other day: compare echo now | cat - /dev/tty | sed -n w\ /dev/fd/1 | cat - /dev/tty to echo when\? | cat - /dev/tty | sed -n p | cat - /dev/tty. Remember that GNU cat never buffers, and I only stuck them in there to demo sed's behavior in the middle of a pipeline with an unending stream. – mikeserv May 26 '15 at 16:39
  • @don_crissti - right - write has to take effect immediately because read takes effect at the time the output is written, not the time the r command is applied - which could enable a particularly masochistic seder to recursively edit one or more files. Anyway, the input is always line-buffered (without -u or -z), and so you can always also line-buffer output by getting specific about the write files. I've been working on-and-off on a simple, shell-agnostic readline replacement for a long time and that helps. – mikeserv May 26 '15 at 18:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.