I am trying to update a file on a server where the new line needs to have a single tab at the front but new to bash scripting and sed and keep getting an unwanted character at the front.

This is the sed command:

sed -i "/acl \"trusted\" {/a&\t$ACL_IP;    # $SRV_HOST_NAME" $ACL_CONF_FILE

In the results I get :
&;    # SERVER;    # SERVER1;    # SERVER2

Instead of :;    # SERVER;    # SERVER1;    # SERVER2

I also tried the following but get an unknown command error:

sed -i "/acl \"trusted\" {/$ACL_IP    # $SRV_HOST_NAME;\r\n\tacl \"trusted\" {/" $ACL_CONF_FILE

I have tried also:

sed -i "/acl \"trusted\" {/a/\t$ACL_IP;    # $SRV_HOST_NAME" $ACL_CONF_FILE

But get:

/;    # SERVER

hoping I am close but just can't see to get rid of the extra character.


So, you are trying to use the (GNU extension) a text command to append text after a match. However, your text in this case starts with a tab character, represented in sed by the escape sequence \t.

Unfortunately there is also a (POSIX compliant) form of the append command in which a backslash character separates the a command from the text. So when you write a\t it appends a literal t. You "fixed" that by placing an additional character (first &, then later /) to separate the backslash from the a command - but that just caused those characters to be appended literally.

To resolve the ambiguity, you need to pass the command as a\\t. Unfortunately, because you are using double quotes around the whole sed expression (to allow for expansion of shell variables), the shell will expand \\ to \; to pass \\ you need to escape both backslashes:

sed "/acl \"trusted\" {/a\\\\t$ACL_IP;    # $SRV_HOST_NAME"

Alternatively, single-quote the parts that don't need shell expansion:

sed '/acl "trusted" {/a\\t'"$ACL_IP;    # $SRV_HOST_NAME"

Another option is to replace the escape sequence \t by a literal tab, composed using Ctrl+V then TAB. You must still use the a\ form of the command, since the GNU extension a text form ignores leading whitespace.

Alternatively (and perhaps more cleanly) you could use the r command to read a printf-formatted string from standard input:

printf '\t%s;    # %s\n' "$ACL_IP" "$SRV_HOST_NAME" |
  sed -i '/acl "trusted" {/r/dev/stdin' "$ACL_CONF_FILE"
  • Thanks steeldriver. that is an excellent explanation and makes perfect sense, many thanks. – freddy2020 Jan 22 at 15:34
  • @freddy2020 you're welcome - I've added another alternative that avoids escaping/quoting issues of the a command altogether – steeldriver Jan 22 at 16:57
  • Thanks @steeldriver. I am going to have a look at the printf command. – freddy2020 Jan 23 at 11:48
  • New to bash scripting and only book I have far is full of mistakes and that is where I got it from but want to learn how to write better code if mine wasn't good? – freddy2020 Jan 23 at 11:54
  • @freddy2020 I wouldn't call it bad - you just ran across a tricky edge case (using an ANSI escape sequence in a context where the backslash character has another interpretation). For bash itself, my recommended resource is this BashGuide. For sed I find Sed One-Liners Explained useful although I'm not sure it covers your particular use-case. – steeldriver Jan 23 at 13:44

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.