0

I build the following sed cli in order to add the following line

force_https_protocol=PROTOCOL_TLSv1_2

after the line:

[security]

the sed cli :

sed  -i '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2'  /etc/ambari-agent/conf/ambari-agent.ini

but the problem is when the line - force_https_protocol=PROTOCOL_TLSv1_2 is already after [security]

how to change the sed syntax in order to ignore adding the line if line already exist?

expected output

[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

wrong file:

[security]
force_https_protocol=PROTOCOL_TLSv1_2
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
muru
  • 72,889
yael
  • 13,106
  • Is it guaranteed that force_https_protocol=PROTOCOL_TLSv1_2, when already present, will only appear on the lines that immediately follow [security]? In other words, can we assume that instances of force_https_protocol=PROTOCOL_TLSv1_2 that appear elsewhere after [security] (if any) are not a concern to you? – fra-san Mar 15 '20 at 20:26
  • yes - we not need to append if the line is after [security] , or if the line is somewhere not after [security] , then we need to append – yael Mar 15 '20 at 20:29

2 Answers2

0

Assuming that all "force_https_protocol=PROTOCOL_TLSv1_2" in file are within the [security] tag and do not appear elsewhere

sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
    -e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file

The idea is to delete all lines in file containing (only) the "force_https_protocol=PROTOCOL_TLSv1_2" string.

Explanation

/^force_https_protocol=PROTOCOL_TLSv1_2$/d'

Delete all lines containing the "force_https_protocol=PROTOCOL_TLSv1_2" string.

Tests

$ cat file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

$ sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
      -e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
Paulo Tomé
  • 3,782
0

1. Using AWK

Here is a possible solution to the more general problem of adding a string to a labeled block of text (i.e. the lines of text that follow a specific label in a file and precede the next label or the ond-of-file) only if string is not present yet.

A simple approach is to walk the file backwards, keep track of the last time we encountered string, print it every time we encounter a specific label (just before printing the label itself), but only if we haven't seen it (string) yet, and forget about having encountered it (string) any time we come across a label. And, of course, reverse the result as the last step.

tac file | awk '
    /^force_https_protocol=PROTOCOL_TLSv1_2$/ {
        seen = 1
    }
    /^\[security\]$/ {
        if ( ! seen ) {
            print "force_https_protocol=PROTOCOL_TLSv1_2"
        }
    }
    /^\[[^]]*\]$/ {
        seen = 0
    }
    1' | tac

tac, included in GNU Coreutils, concatenates and reverses files line by line. How to reverse a file with other/standard tools is covered in several Q/As on U&L (for instance, How does the command sed '1!G;h;$!d' reverse the contents of a file? and Reverse grepping).

This will not insert force_https_protocol=PROTOCOL_TLSv1_2 after the first [security] in the following text snippet:

[security]
keysdir=/var/lib/ambari-agent/keys
force_https_protocol=PROTOCOL_TLSv1_2
passphrase_env_var_name=AMBARI_PASSPHRASE

[security] ...


To simply insert string after every occurrence of a label unless string is already there, the same approach may lead to:

tac file | awk '
    /^force_https_protocol=PROTOCOL_TLSv1_2$/ {
        seen = NR
    }
    /^\[security\]$/ {
        if ( ( NR == 1 ) || ! ( NR == seen + 1 ) ) {
            print "force_https_protocol=PROTOCOL_TLSv1_2"
        }
    }
    1' | tac

(This will insert force_https_protocol=PROTOCOL_TLSv1_2 after [security] in the above text sample).

2. Using sed

With sed you can use a variation of the N;P;D; cycle (which is abundantly covered in other Q/As on U&L; for instance: How can I use sed to replace a multi-line string? or How to edit next line after pattern using sed?).

This will insert the line force_https_protocol=PROTOCOL_TLSv1_2 after a line composed solely of [security] only if the former is not already there.

sed '
  $! {
    N
    P
    /^\[security\]\n/ {
      /\nforce_https_protocol=PROTOCOL_TLSv1_2$/ b b
      i\
force_https_protocol=PROTOCOL_TLSv1_2
    }
    :b
    D
  }
  s/^\[security\]$/&\
force_https_protocol=PROTOCOL_TLSv1_2/'

This script:

  • for each line, except for the last one ($!), appends a newline followed by the next line to the pattern space (N) (which already contains the current line);
  • prints the first line in the pattern space (P); if that line is [security]...
    • ...and the next line is force_https_protocol=PROTOCOL_TLSv1_2, just deletes the first line from the pattern space and starts a new cycle (b b, :b D);
    • otherwise, prints force_https_protocol=PROTOCOL_TLSv1_2 to standard output (i\) before deleting the first line from the pattern space and starting a new cycle (D);
  • if the last line is [security], replaces it with itself followed by a newline and force_https_protocol=PROTOCOL_TLSv1_2 and prints the result (by means of the implied p).
fra-san
  • 10,205
  • 2
  • 22
  • 43